深入理解Java虚拟机笔记(4)-------垃圾回收算法
分代收集理论的三条假说
- 弱分代假说:绝大多数对象都是朝生夕灭的(大多数对象(大概98%)第一次垃圾回收时就被回收掉了)
- 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡(eg:经过几次垃圾回收都没有被回收的对象,下一次回收很可能也不会回收它)
- 跨代引用假说:跨代引用相对于同代引用来说仅占极少数(跨代引用就是新生代和老年代的对象之间有引用关系,有这种跨代引用的对象很少)
接下来主要说三种垃圾收集算法: 标记-清除算法,标记-复制算法,标记-整理算法。
1. 标记-清除算法:
该算法分为标记和清除两个阶段:根据垃圾回收判断算法先标记出所有需要回收的对象,然后再回收他们;或者反过来标记所有不需要回收的对象,然后回收所有没有标记的对象。
标记-清除算法明显的缺点有两个:
- 效率不稳定:如果内存中有大量需要回收的对象,那么垃圾回收器就要做大量的标记-清除工作;也就是说算法的效率随需要回收对象的数量的增长而增长。
- 内存空间碎片化问题:如果需要回收的对象散落的分布在内存中,回收之后内存中会产生很多不连续的碎片,当再需要分配一块较大的连续内存的时候就无法满足,进而提早进行下一次的垃圾回收。
2. 标记-复制算法:
标记-复制算法将内存划分为相等的两块,每次只使用其中一块。当一块的内存空间不足的时候,就把所有存活的对象复制到另一块内存中,然后把使用过的内存一次性都清理掉。这样在一般情况下(弱分代假说)只需要复制少数的存活对象,而且不会产生内存碎片。但是缺点也很明显:可用的内存空间变成了一半。
HotSpot虚拟机的一些新生代垃圾收集器就采用这种算法,不过内存并不是五五开的,而是分为一个Eden和两个Survivor(根据弱分代假说,分配比例为8:1:1),假设内存为10,就分配大小为8的Eden和两个大小为1的Survivor,每次只用Eden和其中一个Survivor1分配内存,当内存不足需要回收时,就把用过的Eden和Survivor1中的存活对象复制到Survivor2中,然后清除Eden和Survivor1,再用Eden和Survivor2分配内存。这也就是为什么需要两个Survivor的原因(只有一个的话用来分配的内存和用来复制的内存的角色无法切换)。而为了应对存活的对象超过10%的情况,当Survivor空间不足以容纳一次Minor GC(新生代垃圾回收)之后存活的对象时,就需要依赖其他内存区域(实际上大多就是老年代)进行分配担保。
3. 标记-整理算法:
标记-整理算法大体上与标记-清除算法一样,但是当标记完之后并不是直接回收死亡的对象,而是把所有存活的对象移动到内存的一端,然后回收边界意外的所有内存。这种算法的缺点也较为明显,就是移动对象需要额外的开销
HotSpot虚拟机里面关注吞吐量的ParallelScavenge收集器是基于标记-整理算法的,而关注延迟的CMS收集器则是基于标记-清除算法的。