栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

jvm总结

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

jvm总结

1. 内存模型

        线程私有:

        1)程序计数器:指示程序执行位置、便于流程控制;线程切换时记录程序执行位置

        2)虚拟机栈:方法执行内存模型(一次方法执行对应一个栈帧),包括局部变量表、操作数栈

        3)本地方法栈:调用native方法,创建的局部变量

        线程公有:

        1)堆:存放对象实例 可分为

        对象头:锁、GC状态、类型指针;实例数据;对象填充(对其8个倍数)

        堆内存采用分代划分:主要划分为新生代和老年代,新生代又划分为eden、survivor区,整体比例为8:1:1

        2)方法区:类信息、常量池

                ps:class文件常量池:常量的字面量放到class文件中

                        运行时常量池:class常量池加载到内存的方法区

                        字符串常量池:内存堆中,全局,采用hash表保存字符串引用,值在堆中;intern         方法查找池中常量

        3)直接内存:便于NIO类使用的内存

2. 类加载

        加载:1)通过类名获取字节流 2)将文件中静态存储结构转变为方法区的运行时数据结构                         3)生成class对象,作为方法区类数据的访问入口

        链接:

                1)验证:文件格式、元数据、字节码、符号引用校验

                2)准备:为类变量分配内存,设置零值

                3)解析:将常量池中的符号引用转换为直接引用

        初始化:

                执行类构造器方法(cinit),此方法自动收集类变量赋值、静态代码块语句,代码顺序执行

        类加载器:

        1)内置三个内加载器:启动类加载器(bootstrapclassloader)、最顶层类加载器,由c++实现,负责加载lib目录的jar;扩展类加载器(extensionclassloader),加载ext目录下的jar;应用程序类加载器(applicationclassloader),加载当前classpath的所有jar

        2)双亲委派机制:在类加载过程中先判断是否被加载,若无则将请求委托给父类加载器,若父类加载器无法处理才由自己处理 目的:保证核心类库安全,使其不会被自定义类加载器加载

        破坏双亲委派机制

        1)SPI机制:以java.sql.Driver为例,Driver是jdk提供的一个接口,各大数据库厂商提供具体实现类,但是依据双亲委派机制,由引导类加载的Driver无法拿到由应用程序加载器加载的实现类,但是通过线程上下文类加载器可以实现

        2)web应用类加载器:每个应用对应一个类加载器,先尝试自己加载,随后给父类加载器加载(保证用户类优先级高于容器提供的类)

JVM对象

        对象创建:

        1. 类加载检查:检查类是否已经被加载过

        2. 分配内存:

                1)堆内存规整:指针碰撞,空闲内存放一边,移动分界指针即可

                2)堆内存不规整:空闲列表,维护一个内存可用列表

          3. 初始化零值(变量赋为零值)

          4. 设置对象头

          5. 初始化:执行方法(语句块、变量初始化、构造函数)’

        ps:父类静态属性>子类静态属性>父类非静态属性(按代码顺序执行)>子类非静态属性

        对象访问:

        java程序访问时通过栈上reference数据结构操作对象,访问对象主要有两种方式:

        1)句柄:在堆内存中划分一块句柄池,reference指向句柄池中地址,句柄负责指向对象内存地址和类对象地址

        2)直接指针:reference指向对象地址,但还需通过对象去访问类对象

        句柄的好处时reference存储的是稳定的句柄地址,在对象移动的时候只需要改变句柄对象指针即可;使用直接指针的好处是速度快,接生了一次指针定位的开销(hotspot使用直接指针)

GC:

判断对象是否回收:

1. 引用计数:

        每个对象都有一个引用计数器,若被引用则加一,若为零则可被释放

        优点:简单高效; 缺点:不能解决循环依赖

ps:强引用:不能被GC 软引用:内存不足的时候被回收 弱引用:GC时一定被回收;虚引用:和无引用一样,只是GC时受到系统通知

2. 可达性分析:

        以GCRoots为起点搜索,搜索不到的则可被释放

        GCRoots:1)栈帧中局部变量  2)方法区中的静态变量和常量 3)本地方法栈JNI引用对象

优点:更加精确,解决了循环依赖问题

        具体来说其实经历了两次标记,首先可达性分析标记可回收对象,如果要执行finalize,则将其放入队列中,随后进行第二次标记,判断在执行finalize,是否被其他引用

缺点:实现复杂;可达性分析耗时,甚至STW

HotSpot具体实现:

1)GCRoots一般存在方法区和栈帧中,如果每次GC都去全局查找GCRoots则太费时间,因此采用空间换时间的思想,用一个oopMap结构来记录内存那个位置是GCRoots,这样GC时就不用查找

2)许多条命令的执行都会导致OopMap结构数据发生变化,如果为每次执行这些命令都生成OopMap的话,太耗费空间,因此只在指定时间点记录信息,这些时间点称为安全点

3)目前主要有两种方式让所有线程跑到最近的安全点,然后停顿下来

        抢先式中断:首先将线程全部中断,然后再检查哪些没有到达安全点 ,没有到达安全点的就恢复线程,让他跑到安全点(几乎不用)

        主动式中断:不直接对线程操作,仅仅简单的设置一个标志(共一个标志),各个线程执行时在安全点主动轮询这个标志,发现中断标志为真是就自己挂起

        使用safepoint之后还可以进行改进,即使用“安全区域”,所谓安全区域指的是一段代码,在这段代码中,引用关系不变,因此可以生成OopMap并进行GC。当线程执行到SafeRegion后如果要离开时则要等待GC完成

准确GC?

垃圾回收算法:

2.1 标记-清除算法

        首先标记所有需要回收的对象,标记回收后统一清理

        缺点:1)效率问题:标记与清除过程效率不高 2)空间问题:标记清除产生大量不连续碎片

2.2 复制算法

        将可用内存划分为大小相等的两块,每次只使用其中一块,当这块内存用完之后,就将还存活的对象复制到另一块,然后将已使用过一块直接清理

        改进算法:将内存分为两部分,一部分是较大的Eden(占80%)和两个survivor区(10%),每次使用eden和一个survivor区,每次回收时将eden和survivor的活对象复制到另一个survivor区中,也就是只有10%的空间会被浪费

        内存分配担保:并不是每次活着的对象都少于10%,因此如果另一个survivor空间没有足够空间存放存活对象将对象存放到老年代(即用老年代来担保)

        优点:解决了内存碎片,可高速分配连续空间

        缺点:浪费部分空间,使用效率低

        

2.3 标记-整理算法

        首先先标记,然后将所有存活的对象都向一端移动,随后直接清理端边界以外的内存即可

        优点:可有效利用堆空间,并且无内存碎片

        缺点:复制开销大

垃圾收集器:

1)Serial、Serial Old

串行收集器,在进行收集的时候必须其他线程直到GC完成,

适用于单核或者比较小的堆中

2)Parallel Scavenge、Parallel Old

jdk1.8默认收集器组合

ps:新生代收集器全使用复制算法,老年代传并行收集器使用标记整理,CMS使用标记清除

G1适用于新生代、老年代

        

        

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/351328.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号