跳转至

G1:现代默认垃圾收集器#

版本信息

从 JDK 9 开始,什么都不配,默认就是 G1。它是当前 Java 通用场景的「安全默认值」:堆切成区域(region)、优先回收垃圾最多的区域,靠停顿预测模型把单次回收控制在目标内,且因为是复制式回收、天然无碎片——正好补上了 CMS 的两个死穴。本页讲清它的原理、参数和「为什么多数时候你该信任它」。

设计目标:停顿可控 + 无碎片#

G1(Garbage First)的设计目标是在较大堆上实现可预测的停顿,同时避免碎片化

  • 堆 region 化:把堆切成大量大小相等的 region(默认 1–32MB),每个 region 动态充当 Eden / Survivor / Old / Humongous。年轻代不再是连续的一大块,而是「一组 region」的逻辑集合。
  • Garbage First:G1 跟踪每个 region 的存活对象量(即「垃圾比例」),优先回收垃圾最多的 region——用最少的回收工作释放最多的空间。
  • 停顿预测模型:根据 -XX:MaxGCPauseMillis 目标和历史耗时数据,预测回收每个 region 的成本,挑出「能在目标停顿内完成」的 region 组成回收集(CSet)。这是 G1 停顿可控的核心。
  • 复制式回收 → 无碎片:把 CSet 里的存活对象复制到空 region、原 region 整个释放。因为总是复制整理,不存在 CMS 那样的碎片化

工作原理:Young GC + 并发标记 + Mixed GC#

graph TD
    A["分配压力"] --> YG["Young GC(STW)<br/>回收年轻代 region"]
    YG -->|"堆占用达 IHOP(默认 45%)"| CM["并发标记<br/>统计各 region 垃圾比例(SATB)"]
    CM --> MX["Mixed GC(STW)<br/>年轻代 + 垃圾多的老年代 region"]
    MX --> YG
    MX -->|"跟不上分配"| FG["Full GC(兜底)<br/>JDK 11 起并行"]
    style YG fill:#bbdefb
    style MX fill:#c8e6c9
    style FG fill:#ffcdd2
  • Young GC(STW,频繁):Eden region 用尽时触发,复制存活对象到 Survivor / Old region,回收年轻代。这是日常、短停顿的回收。
  • 并发标记(与应用并发):当堆占用达 IHOPInitiatingHeapOccupancyPercent,默认 45%)启动,用 SATB(开始时拍快照 + 写屏障记录变更)统计每个老年代 region 的存活对象量,为下一步挑 region 做准备。
  • Mixed GC(STW):在停顿目标内,回收全部年轻代 + 若干垃圾比例高的老年代 region。多次 Mixed GC 逐步清理老年代,而非一次性整堆。
  • Full GC(兜底):当 Mixed GC 跟不上分配速率时触发。JDK 10 前 Full GC 是单线程(Serial),JEP 307(JDK 10)改为并行(JDK 11 LTS 承接),worst-case 停顿大幅缩短。
graph LR
    subgraph H["G1 堆 = 多个 region"]
      E["Eden region"] --- S["Survivor region"]
      S --- O["Old region"]
      O --- HU["Humongous region<br/>(大对象,≥1/2 region)"]
    end
    E --> CSet["回收集 CSet<br/>按垃圾比例 + 停顿目标挑选"]
    O --> CSet
    CSet --> P["复制存活对象到空 region<br/>原 region 整体释放 → 无碎片"]
    style CSet fill:#ffe0b2
    style P fill:#c8e6c9

JVM 配置参数#

启用#

java -jar app.jar                              # JDK 9+ 默认就是 G1,无需指定
java -XX:+UseG1GC -Xmx8g -jar app.jar          # 显式指定

关键调优参数#

参数 含义 默认 备注
-XX:MaxGCPauseMillis=<ms> 期望最大停顿目标 200 G1 据此挑 CSet;设太小会频繁 GC、吞吐降
-XX:G1HeapRegionSize=<size> 单个 region 大小 -Xmx 自动选(1/2/4…32MB) 影响 Humongous 判定与大对象处理
-XX:InitiatingHeapOccupancyPercent=<n>(IHOP) 堆占用达 n% 启动并发标记 45 JDK 9 起默认自适应(按历史调整)
-XX:G1NewSizePercent=<n> 年轻代最小占比 5 想给年轻代保底用
-XX:G1MaxNewSizePercent=<n> 年轻代最大占比 60 限制年轻代上限
-XX:G1ReservePercent=<n> 保留空闲堆占比(防疏散失败) 10 频繁疏散失败(evacuation failure)时可调大
-XX:ParallelGCThreads=<n> STW 阶段并行线程数 ≈ CPU 核数 容器内注意限额
-XX:ConcGCThreads=<n> 并发标记线程数 ParallelGCThreads/4 占用应用 CPU,过多影响吞吐
-XX:+G1EnableStringDeduplication 字符串去重(相同内容共享) 字符串密集场景省内存
# 典型:通用服务,8GB 堆,停顿目标 100ms
java -XX:+UseG1GC -Xmx8g \
     -XX:MaxGCPauseMillis=100 \
     -XX:InitiatingHeapOccupancyPercent=45 \
     -XX:G1ReservePercent=15 \
     -jar app.jar

各版本 G1 增强#

版本 JEP 增强
JDK 9 248 G1 成为默认;IHOP 自适应
JDK 10 307 并行 Full GC(此前单线程兜底),JDK 11 LTS 承接
JDK 12 344 可中止的混合回收(Abortable Mixed Collections),必要时提前中止以满足停顿目标
JDK 14 345 NUMA 感知,多 socket 机器上局部性更好
JDK 12 346 检测空闲并及时归还未用堆内存给操作系统

适用场景#

  • 通用首选:从 JDK 9 起,绝大多数服务端应用用默认 G1 就对。
  • 较大堆(> ~4–6GB):G1 的 region 化 + 停顿预测在大堆上优势明显。
  • 停顿要可控但不要求亚毫秒:G1 能把停顿压在几十到一两百毫秒量级(视堆和负载)。若要亚毫秒,上 ZGC

与前代 GC 对比:CMS / Parallel → G1#

G1 接过 CMS「可控停顿」的衣钵,又补上它最大的短板:分区复制 → 无碎片。相对被它取代的 CMS(标记-清除、碎片化、退化长 Full GC),G1 用 region + 停顿预测把停顿做得可预测且不退化;相对 JDK 8 默认的 Parallel(全 STW、Full GC 整堆长停顿),G1 的停顿可控得多。

java -XX:+UseConcMarkSweepGC -Xmx8g -jar app.jar   # 停顿较低,但碎片化→退化长 Full GC
java -XX:+UseParallelGC -Xmx8g -jar app.jar        # 吞吐高,但 Full GC 整堆长停顿
java -jar app.jar                                  # 默认 G1,停顿可控、无碎片
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar app.jar

常见坑 / 最佳实践#

  • 别设太激进的 MaxGCPauseMillis:设到几毫秒 G1 也做不到,只会让 CSet 缩得很小、GC 更频繁,吞吐反降。从默认 200 开始,按观测调整。
  • 别给 G1 设固定年轻代大小-Xmn-XX:NewRatio-XX:NewSize对 G1 基本无效或会警告——G1 要靠动态调整年轻代 region 数来满足停顿目标,手动固定会破坏这套机制。
  • 频繁 evacuation failure / to-space exhausted:说明 Mixed GC 时没有空 region 放存活对象。调大 -XX:G1ReservePercent(如 15–20),或增大 -Xmx
  • 偶尔的 Full GC 是兜底信号:G1 正常应靠 Young/Mixed GC 维持。频繁 Full GC 说明分配速率超过回收能力,需调 IHOP、增大堆,或排查对象分配热点。
  • ConcGCThreads 别设太大:并发标记线程和应用争 CPU,过多会拖累吞吐。
  • Humongous 对象:超过 region 一半的大对象走 Humongous region,频繁分配大对象(如大数组)会拖慢 G1——必要时调大 G1HeapRegionSize

小结#

G1 是 JDK 9 以来「停顿可控、无碎片、大堆友好」的默认选择。它用 region 化 + 停顿预测模型把单次回收约束在目标内,用复制式回收根除碎片化,并在 JDK 11/12 持续增强(并行 Full GC、可中止混合回收、NUMA 感知)。对绝大多数应用,信任默认的 G1、配合观测按需微调即可;只有对延迟有极致要求时才需考虑 ZGC。

参考#