本文涵盖几种常用的垃圾回收算法的简单介绍,包括:
内容和截图全部来自《实战Java虚拟机:JVM故障诊断与性能优化(第2版)》
标记清除法是现代垃圾回收算法的思想基础。标记清除法将垃圾回收分为两个阶段:标记阶段和清除阶段。
在标记阶段,首先通过根节点标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。标记清除法的最大问题是可能产生空间碎片。
复制算法的核心思想是:将原有的内存空间分为两块,每次只使用其中一块,在进行垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量就会相对较少。因此,在真正需要垃圾回收的时刻,复制算法的效率是很高的。
又由于对象是在垃圾回收过程中统一被复制到新的内存空间中的,可确保回收后的内存空间是没有碎片的。虽然有以上两大优点,但是复制算法的代价却是将系统内存折半,因此,单纯的复制算法也很难让人接受。
在Java的新生代SerialGC回收器中,使用了复制算法的思想。新生代分为eden区、from区和to区3个部分。其中from区和to区可以视为用于复制的两块大小相同、地位相等且可进行角色互换的空间。from区和to区也称为survivor区,即幸存者空间,用于存放未被回收的对象。
在进行垃圾回收时,eden区的存活对象会被复制到未使用的survivor区(假设是to区),正在使用的survivor区(假设是from)的年轻对象也会被复制到to区(大对象或者老年对象会直接进入老年代,如果to区已满,则对象也会直接进入老年代)。此时,eden区和from区的剩余对象就是垃圾对象,可以直接清空,to区则存放此次回收后的存活对象。这种改进的复制算法,既保证了空间的连续性,又避免了大量的内存空间浪费,如下图所示。
标记压缩法是一种老年代的回收算法。它在标记清除法的基础上做了一些优化。和标记清除法一样,标记压缩法首先也需要从根节点开始,对所有可达对象做一次标记。但之后,它并不只是简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。然后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,性价比较高。
由于新生代和老年代的特点不一样,所以针对不同的代使用不同的算法。
新生代一般使用复制算法,例如以下的垃圾收集器就是基于复制算法的:
而老年代一般使用标记清除或标记压缩算法,例如以下的垃圾收集器:
分区算法将整个堆空间划分成连续的不同小区间,如下图所示。每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收小区间的数量。
一般来说,在相同条件下,堆空间越大,一次GC所需要的时间就越长,从而产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标停顿时间,每次合理地回收若干个小区间,而不是回收整个堆空间,从而减少一次GC所产生的停顿。
G1回收器(GarbageFirst)是在JDK1.7中正式使用的全新的垃圾回收器,从长期目标来看,它是为了取代CMS回收器。G1回收器拥有独特的垃圾回收策略,和之前提到的回收器截然不同。
从分代上看,G1依然属于分代垃圾回收器,它会区分年轻代和老年代,依然有eden区和survivor区,但从堆的结构上看,它并不要求整个eden区、年轻代或者老年代都连续。
它使用了分区算法。作为CMS的长期替代方案,G1使用了全新的分区算法,其特点如下:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/daguanjia11/article/details/120676305
内容来源于网络,如有侵权,请联系作者删除!