Java的垃圾收集器随着JDK的更新是一直不断演进的,基本的回收器实现可以分为串行收集器,并行收集器,CMS GC,G1 GC,ZGC等多种不同的实现。
其中年轻代回收算法 Serial(Serial Old属于老年代的标记-整理算法)、ParNew、Parallel Scavenge 都是复制算法,而 CMS、G1、ZGC 都属于标记清除算法,在老年代使用。以下我们分别介绍一下这几种垃圾收集器。
Serial GC
它是最古老的垃圾收集器,Serial”体现在其收集工作是单线程的,并且在进行垃圾收集过程中,会进入臭名昭著的“Stop-The-World”状态。
Serial GC还有一种采用的是标记-整理算法,一般将其称之为Serial Old GC算法。
Serial GC 的对应 JVM 参数是:-XX:+UseSerialGC。
ParNew GC
很明显是个新生代 GC 实现,它实际是 Serial GC 的多线程版本,最常见的应用场景是配合老年代的 CMS GC 工作。
ParNew GC设置对应的参数是:-XX:+UseConcMarkSweepGC -XX:+UseParNewGC。
Parallel GC
在JDK8的早期版本中,它是 server 模式 JVM 的默认 GC 选择,也被称作是吞吐量优先的 GC。特点是新生代和老年代的GC都是并行执行的。
Parallel GC设置对应的参数是:-XX:+UseParallelGC
CMS(Concurrent Mark Sweep) GC
CMS算法基于分代回收理论,CMS使用标记清除算法,并发收集停顿小,整个回收过程如下图所示:
image.png
1)初始标记,这个阶段会 stop the world(STW),标记的对象只是从 root 集最直接可达的对象;
2)并发标记,这个阶段GC线程和应用线程并发执行,标记了所有的可达对象。
3)重新标记,这里是第二个 stop the world(STW)阶段,停顿时间比并发标记短但是比初始标记长,这里需要对对象进行重新扫描和标记。
4)并发清理,这里进行并发的垃圾清理。
5)并发重置,为下一次GC重置相关数据结构。
G1 GC
G1 GC是一种兼顾吞吐量和停顿的实现。他仍然保留了对象分代的概念,但是这里已经没有原来的那种严格的物理区隔,而是分为一个个小的Region,一部分区域用作年轻代,一部分用作老年代,另外还有一种专门用来存储巨型对象的分区,如下图所示:
image.png
G1 采用每次只清理一部分而不是全部的 Region 的增量式清理,由此来保证每次 GC 停顿时间不会过长。
G1 可以直观的设定停顿时间的目标,相比于 CMS GC,G1 未必能做到 CMS 在最好情况下的延时停顿,但是最差情况要好很多,基于此G1 GC适合对最大延迟有要求的场合。JDK9已经将G1 GC设置为默认的GC算法。
ZGC
ZGC是JDK11才引入的,他针对大堆内存(TB级别)的设计,据说可以做到10ms以下的回收停顿时间。基于此ZGC 适用于 64 位系统的大内存服务中。
ZGC中引入的一些新特性
在ZGC中已经没有了对象分代的概念。他使用了着色指针的技术和读屏障技术。
另外ZGC 和 G1 一样,也是基于Region的,而且他的Region大小是可以动态变化的,他也会在回收后对 Region 中的对象进行移动合并,解决了碎片问题。
ZGC的回收过程
1)初始时,整个堆空间被划分为大小不等的许多 Region,ZGC 首先会进行一个短暂的 STW,来进行 roots 标记。这个步骤非常短,因为 roots 的总数通常比较小。
2)然后就开始进行并发标记,如上图所示,通过对对象指针进行着色来进行标记,结合读屏障解决单个对象的并发问题。
3)下一个是清理阶段,这个阶段会把标记为不在使用的对象进行回收,如上图所示,把橘色的不在使用的对象进行了回收。
4)最后一个阶段是重定位,重定位就是对 GC 后存活的对象进行移动,来释放大块的内存空间,解决碎片问题。
image.png