JAVA垃圾收集算法总结以及CMS、G1算法详解

从方法论上讲 , 程序语言的回收算法主要分为
一、引用计数算法(Reference Counting):给对象添加一个引用计数器 , 每当一个地方引用它时 , 数据器加1;当引用失效时 , 计数器减1;计数器为0的即可被回收 。
二、根搜索算法(GC Root Tracing):通过一系列的名为“GC Root”的对象作为起始点 , 从这些节点开始向下搜索 , 搜索所有走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何引用链相连时(用图论来说就是GC Root到这个对象不可达时) , 证明该对象是可以被回收的 。
JAVA采用了根搜索算法 , 基本原理根据上面解释应该都能理解 , 基于根搜索方法 , 又有具体实现算法
一、标记-清除算法(Mark-Sweep)
最基础的垃圾收集器算法 , 分为“标记”和“清除”两个阶段 , 先标记处所需要回收的对象 , 标记完成后 , 统一回收掉所有被标记的对象 。
缺点:1)效率问题 , 标记和清除的效率不高 。
2)清除后会产生大量的不连续的内存碎片 , 可能会导致该程序需要为较大对象分配内存时无法找到足够连续的内存 , 不得不提前触发垃圾收集动作 。
二、复制算法(Copying)
将内存容量分成大小相等的两块 , 每次只使用其中一块 , 当一块用完时 , 将还存活的对象复制到另一块去 , 然后把之前使用满的那块空间一次性清理掉 , 如此反复 。
优点:内存分配的时候不用考虑内存碎片问题 , 只移动堆顶指针 , 按顺序分配即可 , 简单高效 。
缺点:内存空间浪费大 , 每次只能使用当前的 能够使用 内存空间的一半;当对象存活率较高时 , 需要有大量的复制操作 , 效率低 。
三、标记-整理算法(Mark-Compact)
标记整理是在标记-清算上改进得来的 , 前面说到标记-清算内存碎片的问题 , 在标记-整理中有解决 。同样有标记阶段 , 标记出所有需要回收的对象 , 但是不会直接清理 , 而是将存活的对象向一端移动 , 在移动过程中清理掉可回收对象 。
优点:解决了之前内存碎片的问题 , 特别是在存活率高的时候 , 效率远高于复制算法 。
四、分代收集算法(Generational Collection)
根据内存对象的存活周期不同 , 将内存划分成几块 , java虚拟机中一般将内存划分成新生代和老年代 , 当新建对象时一般在新生代中分配内存 , 在新生代垃圾收集器回收几次后仍然存活的对象 , 将被移动到老年代 , 或者当大的对象在新生代中无法分配到足够连续的内存空间时也会直接分配到老年代 。
上面四种算法JVM在回收内存时都有采用 , 大多都是复合运用多种算法一起实现垃圾回收 , 具体细节每个算法都可以写很多内容 , 为了不偏题 , 我们这里只写CMS、G1 , 其他的有兴趣可以自己查询资料 。CMS算法主要是应用在分代收集算法的老年代里 , 是从JDK8开始采用 ,  当然默认没有启用 , 如果在开发或生产环境想采用CMS , 可以修改JVM配置-XX:+UseConcMarkSweepGC : 手动指定老年代使用CMS收集器 。
下面进入正题
CMS定义:英文全称Concurrent Mark Sweep , 可以翻译为 并发标记清除 , 是一种以获取最短回收时间为目标的收集器 。这是因为CMS收集器工作时 , GC工作线程与用户线程可以并发执行 , 以此来达到降低收集停顿时间的目的 。
CMS收集器仅作用于老年代的收集 , 是基于标记-清除算法的 , 它的运作过程分为4个步骤:

JAVA垃圾收集算法总结以及CMS、G1算法详解

文章插图
 
  • 初始标记(CMS initial mark)
  • 并发标记(CMS concurrent mark)
  • 重新标记(CMS remark)
  • 并发清除(CMS concurrent sweep)
其中 , 初始标记、重新标记这两个步骤仍然需要Stop-the-world 。初始标记仅仅只是标记一下GC Roots能直接关联到的对象 , 速度很快 , 并发标记阶段就是进行GC Roots Tracing的过程 , 而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录 , 这个阶段的停顿时间一般会比初始阶段稍长一些 , 但远比并发标记的时间短 。


推荐阅读