JVM5——垃圾回收算法

常见的回收算法分析:

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

从根节点开始向下扫描,标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
会产生大量内存碎片;效率不高。

产生大量不连续的内存碎片会导致分配大内存对象时,无法找到足够的连续内存,从而需要提前触发另一次Full GC动作。
JVM5——垃圾回收算法

2.复制算法(Copying)

将整个内存分为大小相等的两部分,一部分用来为对象分配内存,另一部分用来将存活的对象复制过去,方便回收可回收对象(复制过去后,直接回收另一部分的所有对象即可);适用于存活对象少,大量短期对象的情况。(因为复制大量存活的对象很花费时间)
每次都是对一块内存进行操作,不存在碎片的情况。代价是将内存缩小了一半。

目前使用复制算法回收新生代。新生代:Eden:From Survivor:To Survivor=8:1:1
每次使用Eden和其中一块survivor。当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另外一块survivor上,最后清理掉刚刚的Eden和Survivor空间。

会有10%的空间永远用不了,没法保证每次回收都只有不多于10%的对象存活,由此引入了空间分配担保,会跟老年代借一步空间直接进入老年代
JVM5——垃圾回收算法

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

与标记-清除算法相同,都是先标记从GC Roots向下查询到的对象,然后对未标记的对象进行回收。
区别是标记-整理算法最后会让所有存活对象都向一端移动,然后直接清理掉边界以外的内存。减少了内存碎片。
JVM5——垃圾回收算法

4.分代收集算法(Generational Collection)

(当前商业虚拟机均使用分代收集算法)
jvm根据对象的生命周期将堆内存按"代"分为新生代和老年代,根据各个年代的特点采用适当的收集算法,这样有利于提高回收效率和成本。

  • 新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,则使用复制算法;如果对堆内存有一定的了解的话,我们知道新生代内存被分为一个较大的Eden区和两个较小的Survivor区,每次只使用Eden区和一个Survivor区,当回收时将Eden区和Survivor还存活着的对象一次性的拷贝到另一个Survivor区上,最后清理掉Eden区和刚才使用过的Survivor区,Eden和一个Survivor的默认比例是8:1。
  • 老年代中因为对象存活率高、没有足够大的额外空间对存活的对象进行复制,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

参考:《深入理解Java虚拟机:JVM高级特性与最佳实践》