G1收集器

Posted by Pismery Liu on Sunday, September 29, 2019

TOC

垃圾收集器 G1

G1「Garbage First」收集器是继 CMS 收集器的下一代垃圾收集器,在 Java 7 中加入了 JVM 垃圾收集器大家族中;其目的是为了适应服务器中大内存,多 CPU 的场景,同 CMS 收集器 一样,G1 关注最低时延的问题,官方也推荐使用 G1 来替代 CMS 收集器;

G1 收集器的特点

  1. 引入了 Region 来划分堆空间,弱化了分代的概念;
  2. 「软实时」:力求达到暂停时间目标,允许少部分情况超出指定时间;
  3. 垃圾回收从可回收空间最多的 Region 开始,回收更多的堆空间,同时尽可能不超过暂停时间目标;这也是它名字「Grabage First」的由来;

相较于 CMS 的改进

  1. CMS 采用标记 - 清除算法,会产生内存碎片,不利于大对象的分配,导致提前触发 Full GC;G1 则采用了标记 - 整理算法;
  2. CMS 只能用于老年代,而 G1 可用于新生代和老年代;
  3. G1 垃圾收集停顿时间可控,算法会尽可能保证在指定停顿时间内完成垃圾回收,防止垃圾收集突然停顿过长导致应用出现雪崩现象;
  4. 并行与并发:更加充分利用多 CPU 的硬件优势

G1 应用场景

G1 主要应用于多 CPU 的大内存服务器中,在满足低延时的前提下,尽可能满足垃圾回收暂停时间;以下场景适合使用 G1 收集器:

  1. 服务端多核并且应用程序占用大内存,至少 4 G 以上;
  2. 采用 CMS 时产生大量内存碎片;
  3. 需要更可控的垃圾收集时间,避免高并发下出现雪崩现象;

G1 堆内存划分

JVM 对堆内存有着分代划分的概念,目的是为了适应不同类型的对象的垃圾回收;JVM 将堆内存分为新生代和老年代;新生代又分为一个 Eden 区和两个存活区;

在 G1 收集器之前的收集器都是在物理层面划分了堆内存;而 G1 将内存划分为大小相等的 Region,但逻辑上还是有着分代的概念;每个 Region 的大小从 1M 到 32M,且值需要是 2 的幂,Region 个数不能超过 2048 个;

下面两张图,可以更直观的看出 G1 对堆内存的划分

从上图中可以看到,G1 将 Region 分为四种类型:Eden, Survivor, Old, Humongous; Eden,Survivor 则为新生代;Old, Humongous 对应老年代区域;其中 Humongous 代表巨型区,当一个对象大小超过了 Region 大小的一半则将对象视为「巨型对象」,这类巨型对象会直接进入老年代,存放这类巨型对象的区域称为巨型区;

区分出巨型区的原因是 G1 局部的垃圾收集算法是复制算法,整体是标记整理算法,为了避免巨型对象的复制,单独处理;由于巨型对象区的对象都是超过 Region 的一半,巨型区会产生内存碎片;

如果想避免巨型区导致的老年代碎片化问题,可以通过 -XX:G1HeapRegionSize 增加单个 Region 的大小,使得对象不再是巨型对象,采用常规的分配路径。

G1 回收算法

G1 提供了两种垃圾回收模式,Young GC 和 Mixed GC,并且两种模式都会 STW「Stop The World」.

Young GC

Youg GC 过程中,会回收 Eden 区和上次垃圾回收后的 Survivor 区。GC 后的存活对象将被复制或者移动到其他 Region 中;如果是存活超过指定阈值的对象会被提升到老年代的 Region,否则移动到 Survivor 的 Region 并同时将此 Region 加入 CSet 中,为下一次 GC 做准备;

Mixed GC

当执行完一次整个并发标记周期,则会将 GC 模式从 Young GC 转换为 Mixed GC,G1 会有选择的将部分 Old Region 加入都需要回收的集合中;加入 Old Region 的数目由一系列的参数控制;经过多次 Mixed GC 回收了足够的 Old Region后,GC 模式会从 Mixed GC 转换为 Young GC;等到下次并发标记周期完成后,再回到 Mixed GC 执行;

并发标记周期

并发标记周期包含以下几个过程:

  1. 初始化标记阶段(Initial mark phase):对根节点进行标记,该阶段会 STW;
  2. 根区域扫描阶段(Root region scanning phase):扫描上一步被标记的 Survivor 区域,获取对 Old Region 的引用,然后标记 Old Region 被引用的对象;此阶段不会 STW ,会与程序并发运行;且只有本过程执行完后才能执行 Young GC;
  3. 并发标记阶段(Concurrent marking phase):扫描整个堆找到所有可达的对象,此阶段不会 STW 与程序并发运行,并且可以被 Young GC 所中断;
  4. 最终标记阶段(Remark phase):此阶段会 STW 收集并帮助完成标记过程,G1 GC 通过消耗 SATB 的缓冲区,跟踪未访问的对象,处理是否有引用;
  5. 清理阶段(Cleanup phase):此阶段会 STW,G1 GC 会标记完成空闲的 Region 和需要进行 Mixed GC 的 Region;整个过程在返回空闲的空的 Region 后就可以并发的执行。执行完此阶段后,就可以执行 Mixed GC 了。

执行流程图

参考链接