JVM GC 器选型与调优

java-concurrency 📚 learning jvm-gc-select-tune · jvm · gc · serialgc · parnew · parallel · cms · g1 · zgc · shenandoah · tuning

TL;DR(30 秒扫完)

  • 演进:Serial → Parallel → CMS → G1 → ZGC/Shenandoah
  • 权衡三角:吞吐 vs 停顿 vs 空间,不可兼得
  • 三层漏斗:JDK 版本(硬约束)→ 内存规模(G1 4-32GB 最佳)→ 业务特征(吞吐/延迟/空间优先)
  • G1 核心优势:Region 结构 + 可预测停顿 + Mixed GC 避免退化 Full GC
  • ZGC 核心:读屏障+多映射,STW <1ms,堆 8MB-16TB

关键结论

结论 AGC 器没有最好的,只有最适合的——按 JDK 版本定范围,按内存规模定候选,按业务特征定取舍
结论 BG1 是 JDK 9+ 默认,4-32GB 堆最佳,Web 服务通用推荐
结论 CZGC JDK 15 GA,堆 4GB+,STW <10ms,JDK 21 引入分代 ZGC
结论 D核心调优参数 -Xms=-Xmx 避免动态扩缩
结论 E评估靠 GC 日志+JFR,关注 GC 频率、STW P99、FullGC 次数、堆使用率

完整讲解(费曼四步)

STEP 1 · 概念

GC 器选型本质是在吞吐 vs 停顿 vs 空间的权衡三角里做取舍。JDK 版本决定可用 GC 器范围,内存规模决定候选,业务特征决定取舍。

STEP 2 · 大白话

类比:GC 器选型像买车。
- JDK 版本:决定你有哪些车型可选(老车买不到新车款)
- 内存规模:堆 1GB 用 Serial(小车),16GB 用 G1(家用车),64GB+ 用 ZGC(大车)
- 业务特征:批量运输用 ParallelGC(载重),市内通勤用 G1(舒适),长途高速用 ZGC(低延迟)
没有最好的车,只有最适合你行程的车。

STEP 3 · 底层

GC 器演进主线

GC 器引入版本特点现状
SerialJDK 1.2单线程,为快而生小堆单核(<1GB)
ParallelGCJDK 5并行化,求吞吐JDK 8 默认
CMSJDK 5并发标记-清除,求停顿JDK 14 弃用移除
G1JDK 7Region+可预测停顿JDK 9+ 默认
ZGCJDK 15 GA读屏障+多映射,STW <1msTB 级堆首选
ShenandoahJDK 11写屏障+Brooks 转发,STW <10msRedHat 主推

三层漏斗决策

第一层:JDK 版本(硬约束)
  • JDK 8 用不了 ZGC(ZGC JDK 15 GA)
  • JDK 14 之后 CMS 被移除
  • JDK 9+ 默认 G1
  • JDK 21 引入分代 ZGC
第二层:内存规模
  • Serial:<1GB
  • G1:4-32GB 最佳
  • ZGC:4GB+,可到 16TB
  • Shenandoah:32GB+
第三层:业务特征
  • 批处理/后台任务 → ParallelGC(吞吐优先)
  • Web 服务通用 → G1(推荐)
  • 低延迟+大堆 → ZGC
  • 需要并发优先 → Shenandoah

GC 器对比表

GC 器吞吐停顿空间堆规模推荐场景
Serial低长高<1GB嵌入式、单核
ParallelGC高中中4-32GB批处理、后台任务
CMS中短(但有碎片)中已弃用JDK 14 移除
G1中可预测中4-32GBWeb 服务通用
ZGC中极短 <10ms中4GB-16TB大堆、低延迟
Shenandoah中短 <10ms中32GB+并发优先

G1 的核心优势

  • Region 结构:堆切成等大 Region(1MB-32MB),回收选最脏的 Region 优先,避免整堆扫描
  • 可预测停顿:-XX:MaxGCPauseMillis 目标停顿,G1 自动决定每次回收多少 Region
  • Mixed GC:Young + 部分老年代 Region,避免退化到 Full GC
  • 无碎片:Region 内可整理

ZGC 的核心优势

  • 读屏障+多映射:STW 只有 Initial Mark 和 Final Relocate 两瞬间,都 <1ms
  • 堆 8MB-16TB:大堆首选
  • 堆外内存感知:JDK 21+ 可管理 DirectByteBuffer 生命周期
  • JDK 21 引入分代 ZGC(Young/Old 分区)

实战调优参数

G1 关键参数:
-XX:+UseG1GC                          # 启用 G1
-XX:MaxGCPauseMillis=200               # 目标停顿(默认 200ms)
-XX:G1HeapRegionSize=16m               # Region 大小(默认动态)
-XX:InitiatingHeapOccupancyPercent=45  # 并发标记启动阈值(默认 45%)
-Xms4g -Xmx4g                         # 堆大小(建议相等)
ZGC 关键参数:
-XX:+UseZGC                            # 启用 ZGC
-Xms4g -Xmx4g                         # 堆大小(建议相等)
通用必备:
-Xms=Xmx                                # 避免动态扩缩开销
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
-XX:MaxDirectMemorySize=512m            # 堆外内存上限
-XX:+HeapDumpOnOutOfMemoryError         # 救命参数
-Xlog:gc*                               # GC 日志(JDK 9+)

GC 评估手段

GC 日志:
  • JDK 9+:-Xlog:gc*
  • JDK 8:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
JFR 事件:
  • jdk.GCPhasePause:GC 各阶段耗时
  • jdk.GCHeapSummary:堆使用汇总
  • jdk.GCObjectAllocationInNewTLAB:TLAB 分配
  • jdk.Safepoint:安全点事件
生产监控:
  • Prometheus + JVMExporter
  • Grafana 大盘
关键指标:
  • GC 频率(每秒数次 YoungGC 正常,FullGC 应极少)
  • STW P99(生产要求 <200ms)
  • FullGC 次数
  • 堆使用率(FullGC 后仍 >70% 说明有泄漏)
  • Metaspace 使用率

场景选型参考

场景堆大小JDK 版本推荐 GC
嵌入式/单核<1GB任意Serial
批处理/后台4-32GBJDK 8+ParallelGC
Web 服务通用4-32GBJDK 8+G1(推荐)
Web 服务低延迟4-32GBJDK 15+ZGC
大堆服务32GB-16TBJDK 15+ZGC
需要并发优先32GB+JDK 11+Shenandoah

JDK 8 → JDK 17 升级注意点

  • CMS 已弃用(JDK 14 移除),必须换 G1/ZGC
  • 模块系统(JPMS)
  • Finalization 弃用改 Cleaner
  • -Xlog 替代 -XX:+Print*
  • JFR 事件更丰富

STEP 4 · 简化

一句话总结:GC 器没有最好的,只有最适合的——三层漏斗决策,G1 是通用答案,ZGC 是大堆进阶。
记忆口诀:
  • 三层漏斗:JDK → 内存规模 → 业务特征
  • G1 三优势:Region / 可预测 / Mixed
  • ZGC 二件套:读屏障 + 多映射
  • 评估三指标:GC 频率 / STW P99 / FullGC 次数

常见误区

"CMS 还是可用的"
JDK 14 已移除,JDK 17/21 用不了
"G1 可以支持任意堆大小"
G1 在 4-32GB 最佳,超过 32GB 停顿开始退化
"ZGC 需要大堆才用"
ZGC 从 4GB 起也能跑,JDK 21 分代 ZGC 更优
"-Xms 和 -Xmx 分开设没问题"
建议相等避免动态扩缩开销
"G1 停顿时间固定"
G1 是可预测停顿(-XX:MaxGCPauseMillis 是目标),实际由 Region 大小决定

延伸追问

G1 的 Region 和传统分代区别?
G1 是逻辑分代(Region 标记为 Young/Old/Humongous),不是物理分代;回收选任意 Region,避免整堆扫描
ZGC 如何实现 <10ms 停顿?
并发标记+并发整理+读屏障+多映射;移动对象时改映射而非改引用;STW 只有 Initial Mark 和 Final Relocate 两瞬间
如何在生产上评估 GC 是否合适?
GC 日志 -Xlog:gc*、JFR jdk.GCPhasePause、Prometheus + JVMExporter;关键指标:GC 频率、STW P99、FullGC 次数、堆使用率
JDK 8 升级到 JDK 17 要注意什么?
CMS 已弃用(JDK 14 移除)必须换 G1/ZGC;模块系统(JPMS);Finalization 弃用改 Cleaner;-Xlog 替代 -XX:+Print*;JFR 事件更多
什么时候从 G1 换 ZGC?
堆 >32GB、P99 STW 敏感(如支付/实时交易)、JDK 15+、可接受略低吞吐;换 ZGC 前要压测对比吞吐和延迟

速查表

GC 演进: Serial → Parallel → CMS(弃用) → G1 → ZGC/Shenandoah

三层漏斗:
  1. JDK 版本 (硬约束)
  2. 内存规模 (4-32GB 用 G1, >32GB 用 ZGC)
  3. 业务特征 (吞吐/延迟/空间)

场景选型:
  <1GB → Serial
  4-32GB Web → G1
  >32GB 低延迟 → ZGC
  批处理 → ParallelGC

关键参数:
  G1: -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45
  ZGC: -XX:+UseZGC
  通用: -Xms=Xmx (避免扩缩)

评估指标:
  GC 频率 / STW P99 / FullGC 次数 / 堆使用率

Anki 候选卡片

Q: GC 器演进主线?
A: Serial → Parallel → CMS(弃用)→ G1 → ZGC/Shenandoah
Q: G1 三大优势?
A: Region 结构 / 可预测停顿 / Mixed GC
Q: ZGC 核心技术?
A: 读屏障 + 多映射,STW <1ms
Q: G1 关键调优参数?
A: -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45
Q: JDK 8 升级到 JDK 17 最需注意?
A: CMS 已弃用(JDK 14 移除),必须换 G1/ZGC

关联题目

  • 《项目中如何选择垃圾回收器?为啥选择这个?》— Round 1 Q10, ⭐⭐⭐⭐(本轮最佳)
  • 关联知识

    GC 器没有最好的只有最适合的——按 JDK 版本定范围,按内存规模定候选,按业务特征定取舍
    GC 器选型像买车:JDK 版本=车型可选;内存规模=小车/家用车/大车;业务=载重/舒适/低延迟
    ✦ 记 忆 口 诀 ✦
    三层漏斗:JDK 版本(硬约束)→ 内存规模(G1 4-32GB)→ 业务特征(吞吐/延迟/空间)
    关键可视化
    GC 器演进主线
    flowchart LR
      S["Serial<br/>JDK 1.2<br/>单线程"] --> P["ParallelGC<br/>JDK 5<br/>并行高吞吐<br/>JDK 8 默认"]
      P --> C["CMS<br/>JDK 5<br/>并发标记清除<br/>JDK 14 弃用移除"]
      C --> G["G1<br/>JDK 7<br/>Region 可预测停顿<br/>JDK 9+ 默认"]
      G --> Z["ZGC<br/>JDK 15 GA<br/>读屏障+多映射<br/>STW<1ms<br/>TB 级堆首选"]
      G --> Sh["Shenandoah<br/>JDK 11<br/>写屏障+Brooks 转发<br/>STW<10ms<br/>RedHat 主推"]
    三层漏斗决策
    flowchart TB
      L1["第一层:JDK 版本(硬约束)"] --> L1R["JDK 8 用不了 ZGC<br/>JDK 14+ CMS 被移除<br/>JDK 9+ 默认 G1<br/>JDK 21 引入分代 ZGC"]
      L1 --> L2["第二层:内存规模"] --> L2R["Serial:<1GB<br/>G1:4-32GB 最佳<br/>ZGC:4GB-16TB<br/>Shenandoah:32GB+"]
      L2 --> L3["第三层:业务特征"] --> L3R["批处理/后台:ParallelGC(吞吐优先)<br/>Web 服务:G1(推荐)<br/>低延迟+大堆:ZGC<br/>并发优先:Shenandoah"]
    GC 器对比表
    flowchart TB
      subgraph Serial
        S1["吞吐低/停顿长/空间高<br/>堆<1GB<br/>嵌入式、单核"]
      end
      subgraph Parallel
        P1["吞吐高/停顿中/空间中<br/>堆 4-32GB<br/>批处理、后台任务"]
      end
      subgraph CMS
        C1["吞吐中/停顿短但有碎片/空间中<br/>已弃用<br/>JDK 14 移除"]
      end
      subgraph G1
        G1["吞吐中/停顿可预测/空间中<br/>堆 4-32GB<br/>Web 服务通用"]
      end
      subgraph ZGC
        Z1["吞吐中/停顿极短<10ms/空间中<br/>堆 4GB-16TB<br/>大堆、低延迟"]
      end
      subgraph Shenandoah
        Sh1["吞吐中/停顿短<10ms/空间中<br/>堆 32GB+<br/>并发优先"]
      end
    核心调优参数
    flowchart LR
      P["调优参数"] --> M["-Xms=-Xmx<br/>避免动态扩缩"]
      P --> M1["-XX:MaxGCPauseMillis<br/>G1 目标停顿"]
      P --> M2["-XX:ParallelGCThreads<br/>并行 GC 线程数"]
      P --> M3["-XX:ConcGCThreads<br/>并发 GC 线程数"]
      P --> L["GC 日志+JFR<br/>-Xlog:gc*<br/>关注 STW P99/FullGC 次数"]
    知识关系

    ⬆️ 前置(Prerequisite)

    jvm-gc-algorithmsjvm-gc-concurrent

    🔄 延伸(Extends)

    暂无

    ⚡ 对比(Contrast)

    OOM 排查— GC 选型讲'选什么',OOM 排查讲'出问题怎么办'——选型错了会导致 OOM
    🎯 概念 📏 规则 ⚠️ 误区 🔍 追问 ✨ 口诀 共 0 张卡,点击翻面