JAVA垃圾回收算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

     由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。

一、标记-清除算法(Mark-Sweep)

       标记-清除算法是最基础的算法。首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。后面三种算法都是基于标记-清除这种思路并对其缺点改进而得。

  缺点:

  • 效率问题。标记和清除过程的效率都不高。
  • 空间问题。标记清除之后会产生大量不连续的内存碎片,空间碎片太多。当程序在以后的运行过程中需要分配较大对象无法分配到连续内存时,不得不提前触发一次垃圾回收动作。

JAVA垃圾回收算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

二、复制算法(Copying)

       为了解决标记-清除算法的缺陷,复制算法就出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。

     优点不会产生内存碎片。按顺序分配内存即可,实现简单,运行高效。

     缺点内存使用率低。能够使用的内存缩减到原来的一半。

JAVA垃圾回收算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

复制算法的效率跟存活对象的数目多少有很大关系,如果存活对象很多,那么复制算法的效率会大大降低。

现在商业虚拟机都采用复制算法来回收新生代。Eden区和Survivor区(From和To),其中只有Survivor其中的一块区域会浪费,当Survivor空间不够用时,需要依赖老年代进行分配担保(Handle Promotion)机制进入老年代。

(三)标记-整理算法(Mark-Compact)/压缩法

        对于新生代,可以使用复制算法。根据老年代的特点,提出了标记-整理算法,标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

JAVA垃圾回收算法——标记-清除算法、复制算法、标记-整理算法、分代收集算法

(四)分代收集算法(Generational Collection)

      分代收集算法是目前大部分JVM垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周将内存划分为若干个区域,根据各个区域的特点不同,采取最适当的回收算法。一般情况下将堆区域划分为新生代(Young Generation)和 老年代(Tenured Generation / Old)。

(1)新生代

       新生代的特点是每次垃圾回收都有大量的对象需要被回收。目前大部分垃圾收集器对于新生代都采用复制算法,对象多需要复制的次数少。新生代内存区域划分为Eden区和Survivor区(From和To区),比例为8:1:1。两个Survivor区组合实现复制算法。

(2)老年代

      老年代的特点是每次只回收少量对象,所以一般使用的是标记-整理算法