JVM学习笔记-垃圾回收算法

现在流行的商业虚拟机,都是采用“分代收集”(Generational Collection)算法,这种算法只是根据对象存活周期的不同将内存划分为几块。一般把Java堆分为新生代和老年代,这样就可以根据各个年代的特点选择合适的回收算法:

  1. 新生代中,每次GC时大部分对象都将死去,这时选用复制算法,只需要复制少量存活对象就可以完成垃圾回收。
  2. 老年代中,由于对象存活率高,况且没有额外的空间对它进行分配担保,这时可以采用“标记—清除”或者“标记—整理”算法。

1、标记—清除算法

最基础的收集算法就是“标记—清除”(Mark-Sweep)算法,如同它的名字一样,算法分为“标记”和“清除”两个阶段。

  1. 首先标记出所有需要回收的对象,回收的对象的判定规则在上一节JVM学习笔记-如何判断对象是否还存活中已经讲过。
  2. 然后在标记完成后统一回收所有被标记的对象。

这个算法的缺点主要有两个:

  1. 效率问题,标记和清除两个过程的效率都不高。
  2. 空间问题,标记和清除后将产生大量不连续的内存碎片,若在程序运行过程中遇到较大的对象时,会由于无法找到足够的连续内存而提前触发垃圾回收,影响程序的性能。
    以下是标记—清除算法的执行过程:

JVM学习笔记-垃圾回收算法_第1张图片

2、复制算法

复制(Coping)算法的效率有提升,它将可用内存按容量划分为大小相同的两块,每次只使用其中的一块。当一块的内存用完了,就将还存活的对象复制一份到另外一块上面,然后将之前使用的那块内存空间一次性清理掉。
复制算法的优点是不会存在内存碎片的情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。其缺点是会浪费内存空间。
以下是复制算法的执行过程图:
JVM学习笔记-垃圾回收算法_第2张图片
现在的商业虚拟机都是采用复制算法来回收新生代,它们通常将内存划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性的复制到另外一块Survivor空间上,最后清理掉Eden和刚才使用过的Survivor空间。
虽然新生代的对象存活率很低,但是我们并不能保证存放存活对象的那块Survivor空间一定够,这时我们需要依赖老年代进行分配担保(Handle Promotion),也就是当空间不够时这些对象会直接存放至老年代。HotSpot虚拟机的默认Eden和Survivor的大小比例是8:1,也就是只有10%的空间会被浪费。

3、标记—整理算法

复制算法在对象存活率高时进行复制,其效率将会降低,并且需要额外的空间进行分配担保,以应对内存不够存放存活对象的情况。因此对于对象存活率高的老年代中一般不使用复制算法。
在老年代中,推荐使用标记—整理算法(Mark-Compact),标记过程与“标记清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象往一端移动,然后直接清理掉端边界以外的内存,标记—整理算法执行过程如下:
JVM学习笔记-垃圾回收算法_第3张图片
参考:

深入理解Java虚拟机第3章-周志明

你可能感兴趣的:(java基础)