JVM学习(一) 垃圾收集算法

内容来源于《深入理解Java虚拟机》-周志明

判断对象存活算法

1.引用计数算法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器减1;任何时刻计数器为0的对象就是不可能在被使用的。

优点:实现简单,判断效率很高

缺点:很难解决对象之间循环引用的问题

2.可达性分析算法

GC Roots 的对象作为起点,向下搜索,搜索走过的路径成为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象不可用。

可作为GC Roots的对象包括:

· 虚拟机栈中引用的对象

· 方法区中类静态属性引用的对象

· 方法区中常量引用的对象

· 本地方法栈中JNI(即一般说的Native方法)引用的对象

缺点:从GC Root 节点查找引用链,主要从以上几个方面进行逐个检查,这个过程十分耗时

 

对象将要被回收的时机

真正要回收一个对象,至少要经历两次标记过程。如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链。那他将会被第一次标记,并且进行一次筛选。筛选的条件是,此对象是否有必要执行finalize()方法。对象没有覆盖finalize()方法,或者finally方法已经被虚拟机调用过。虚拟机对这两种情况都视为没有必要执行。

如果这个对象被判定为有必要执行finalize()方法,那这个对象将会放置在一个叫做 ReferenceQueue 引用队列之中。并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它。稍后GC 将对ReferenceQueue 中的对象进行第二次小规模的标志。随后该对象就要真正被回收了。

注意:任何一个对象的finalize()都只会被系统自动调用一次。

 

回收方法区

主要回收两部分内容:废弃的常量 和 无用的类

 

垃圾收集算法

1. 标记-清除算法(Mark-Sweep)

算法分为标记和清除两个阶段。首先标记出所有需要回收的对象。在标记完成后,统一回收所有被标记的对象。

有两个不足:

 效率问题:药剂和清除两个过程的效率都不高。

 空间问题:标记清楚之后会产生大量的内存碎片。可能会导致需要分配较大对象时,无法找到足够连续内存,而不得不提前触发另一次垃圾收集动作。

2.复制算法

主要用于回收新生代,由于新生代的对象绝大部分都会回收,从而可将内存分为一块较大的Eden空间和两块较小的Survivor。每次使用Eden和其中一块Survivor。当回收时,将Eden 和 Survivor 中还存活着的对象一次性复制到另一个Survivor 空间上,最后清理掉 Eden 和刚才使用过的Survivor空间(两个来回交替)。HotSpot虚拟机默认EdenSurvivor 的大小比例是 8:1:1。当存活超过15次后,移到老年代。

3.标记-整理算法(Mark-Compact)

首先标记出所有需要回收的对象。而让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

4.分代收集算法

根据对象存活周期的不同,一般将Java堆内存分为新生代和老年代。新生代中每次垃圾收集是都会有大量对象死去,只有少量存活,所以选择复制算法,只要付出少量存活对象的复制成本即可完成收集。老年代对象存活率高,没有额外空间对它进行分配担保。就必须使用“标记-清除”或“标记-整理”算法来进行回收。

 

HotSpot的算法实现       

枚举根节点

        由于可达性分析过程耗时,此外分析过程要必须在一个能确保一致性快照中进行,为了保证对象引用关系不允许变化,要求GC进行时必须停顿所有Java执行线程(Sun 称为Stop The Word),即便在号称几乎不会发生停顿的GMS收集器中,枚举根节点时也是必须停顿的。

        目前主流Java虚拟机都是用准确式GC ,可以直接得知存放对象引用的地方。在Hotspot 实现中,使用一组称为 OopMap(Ordinary Object Pointer Map),在类加载完成时,HotSpot 就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定位置记录下栈和寄存器中哪些位置是引用。

        在OopMap的协助下,HotSpot可以快速且准确的完成GC Root 枚举

安全点

        上述中提到的特定位置被称为安全点(Safepoint),即程序执行时并非在所有地方都能停顿下来开始GC,只是在到达安全点时才能暂停。安全点的选定既不能太少让GC等待时间太长,也不能过于频繁而过分加大运行负荷。因此安全点是以"是否具有程序长时间执行的特征"为标准进行评定的。”长时间执行“最明显特征就是指令序列复用,例如:方法调用、循环跳转、异常跳转等,具有这些功能的指令才会产生安全点。

        安全点时线程中断方案:

        1.抢先式中断

        2.主动式中断

安全区域

        安全区域(Safe Region)指一段代码片段之中,引用关系不会发生变化。在这个区域中的任意地方开始GC都是安全的。

        在线程执行到Safe Region中的代码时。首先标识自己已经进了Safe Region,当这段时间 JVM 要发起GC时,就不用管标识自己为Safe Region状态的线程了。在线程要离开Safe Region时。它要检查系统是否已经完成了根节点枚举(或者是整个GC过程)。如果完成了,那线程就继续执行,否则他必须等待,直到收到可以安全离开Safe Region的信号为止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值