天天看点

2-2 垃圾收集器与内存分配策略 - 垃圾收集算法垃圾收集算法

垃圾收集算法

  • 由于垃圾收集算法的实现涉及到大量的程序细节,而且各平台虚拟机操作内存的方法又各不相同,因此本节不打算过多地讨论算法的实现,只介绍几种算法的思想及发展。

一、标记-清除算法

  • 简述:标记-清除算法是一种基础算法,顾名思义该算法主要包含标注和清除两个步骤,先对需要回收的对象进行标注,然后再清除(标记清除过程在上一节有详细介绍)。
  • 缺点:标注和清除两个步骤的运行效率都不高,并且运行后会产生大量不连续的内存碎片,导致后续创建较大对象时可能会申请不到足够连续的内存空间从而再次触发垃圾回收。
  • 总结:之所以称之为基础算法,是因为后续的垃圾收集算法都是以标记-清除算法为基础,并对其各种缺点进行弥补发展而来。

二、复制算法

  • 简述:为了解决标注-清除算法效率低下问题,一种复制算法出现了,它将内存划分为大小相等的两块内存,每次只使用其中一块,当前内存用完后虚拟机将存活的对象复制到另一块内存上,再将本块内存中的对象全部回收,这样内存分配时就不需要考虑内存碎片的问题,只需要按对象大小将堆顶指针向后移动相应距离,划出对应大小的内存即可;只是本算法的实现需要浪费一半的内存空间,代价过于高昂。
  • IBM公司的专门研究表明,新生代中98%的对象都是朝生夕死,所以不需要按1:1的比例划分内存,而是将新生代内存划分为一个Eden和两个Survivor空间,Eden:Survivor=8:1。每次虚拟机使用Eden和一个Survivor空间(有效内存为90%),当回收时,虚拟机将Eden和Survivor中存活的对象一次性复制到另外一个Survivor内存中,随后将Eden和刚才用过的Survivor内存回收。
  • 98%的对象可回收只是一般场景下,我们没有办法保证每次都只有不超过10%的对象被回收,一旦超出,需要老年代分配担保(Handle Promotion)。

三、标记-整理算法

  • 简述:新生代中大部分对象存活时间都很短,但是老年代则相反,在老年代应用复制算法就需要复制更多存活的对象,导致效率降低;在不浪费一半内存的情况下,在老年代应用Eden和Survivor模式必然需要划分分配担保内存,以应对内存中所有对象都100%存活的极端状态,所以一般老年代不能直接选用复制算法。
  • 根据老年代的特点,有人提出一种标记-整理的算法,先对要回收的对象进行标记,但是,不做回收处理,而是将存活的对象向前移动,然后清理掉边界外的所有对象。(疑问,此处标记到底是标记可回收对象还是存活对象,按书中理解是标记可回收对象,但移动的是未标记的存活对象,删除的是边界外的所有对象,觉得此处标记可回收对象貌似不太合理,唯一想到的原因是存活对象的数量远远超过可回收对象,为了减少标记次数所以此处标记可回收对象。不知道对不对,如果哪位大神清楚,望不吝赐教!)。

四、分代收集算法

  • 简述:当前商业虚拟机的垃圾收集算法都采用分代收集算法(Generational Collection),此算法没有新的思想,只是根据对象存活周期的长短将内存分代处理,一般分为新生代和老年代;新生代对象存活周期短,存活的对象相对较少,虚拟机只需要付出少量存活对象的复制成本就可以完成收集,因此新生代选用复制算法回收内存。老年代对象的存活率很高,并且没有分配担保,因此需要选用标记整理算法回收内存。

感谢阅读:

由于个人水平有限,如果有不对的地方希望各位能够留言指正,谢谢!

参考书籍:

《深入理解java虚拟机》