标记 — 清除算法(Mark Sweep)
最基本的垃圾收集算法,从它的名字就可以看得出来,算法分为“标记”和“清除”两个阶段:首先标记处所有需要回收的对象,在标记完成后统一回收被标记的对象。
标记清除算法主要不足有两个方面:
-
效率低
标记和清除两个阶段的效率都不够高
-
空间碎片问题
标记清除之后会产生大量不连续的内存碎片,如果空间碎片太多,会导致以后程序需要分配打对象时无法找到足够的内存空间,而不得不触发一次垃圾回收的动作
复制算法(Copying)
为了解决标记清除的效率问题,它将内存分为大小相等的两个区域,但是每次只使用其中一块区域。当其中一块区域用完了,就将还活着的对象复制(移动堆顶指针,按顺序分配内存)到另外一块区域上,然后再把已使用的那一块区域空间一次清理掉。
优点:
实现简单,运行高效,不会产生空间碎片问题
缺点:
浪费内存,将内存缩小为了原来的一般。
在对象存活率高的时候效率会变得低下。
虚拟机一般都才用此方法来回收新生代。虚拟机将内存划分成为一块较大的Eden区和两块较小的Survivor区,每次就使用Eden区和一块Survivor区,当回收时,将Eden区和Survivor区还存活的对象一次性复制到另一块Survivor区,最后清理掉Eden区和用过的Survivor区。Hotspot 默认Eden和一个Survivor区的大小比例是8:1,也就是每次新生代的可用内存为整个新生代的90%,也就浪费了10%,这完成是可以接受的。当Survivor区内存不够时,需要依赖老年代区域来进行分担内存分配。
标记——整理算法(Mark Compact)
复制收集算法在对象存活率高的时候效率会变得低下。标记过程和“标记——清除”算法一样,区别是后续步骤不再是直接对对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存。
分代收集算法
在新生代,每次垃圾回收都有大批量的对象死去,只有少量对象存活,就选用复制算法,这样只需要付出少量存活对象的复制成本就可以完成垃圾收集工作。
而老年代中因为对象的存活率高、且没有额外的空间来分担,就必须使用“标记——清除”或者“标记——整理”算法来进行回收。