JVMNotes

第 17 章:G1

zjc 于 2026-01-17 发布

这是《JVM 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 G1(Garbage First)是现代 HotSpot 的主流收集器之一。它把堆划分为多个 Region,优先回收垃圾比例高的区域,通过停顿预测模型尽量满足目标停顿,适合大堆交互服务。

17.1 核心思想

Heap
  |-- Region 0 (Eden)
  |-- Region 1 (Survivor)
  |-- Region 2 (Old)
  |-- Region 3 (Free)
  +-- Region 4 (Humongous)

特点:

特点 说明
Region 化 不要求物理连续分代
垃圾优先 优先回收收益高的区域
停顿预测 根据 pause target 建模型
整理为主 复制存活对象降低碎片
并发标记 识别老年代垃圾

17.2 启用与目标

java -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -Xms4g -Xmx4g \
     -jar app.jar

常见参数:

参数 说明
MaxGCPauseMillis 软停顿目标
G1HeapRegionSize Region 大小
InitiatingHeapOccupancyPercent 触发并发标记的全堆占比
G1ReservePercent 预留空间
ConcGCThreads 并发线程
ParallelGCThreads STW 并行线程

停顿目标是软目标。设置得越小,可能通过减少回收工作量实现,但 GC 更频繁、吞吐更低。

17.3 回收活动

Young GC:

Eden / Survivor Regions 满
  -> STW
     -> 拷贝存活对象到新 Survivor / Old
        -> 回收空 Regions

Mixed GC:

并发标记完成后
  -> 回收 Young Regions
     -> 加上部分高收益 Old Regions

Full GC:

并发标记跟不上 / 晋升失败 / Metaspace 等资源不足
  -> 单线程或多线程 Full GC,具体随 JDK 版本演进

目标是尽量避免 Full GC。

17.4 并发标记周期

Initial Mark
  -> Root Region Scan
     -> Concurrent Mark
        -> Remark
           -> Cleanup
              -> Mixed GC

阶段:

阶段 STW 说明
Initial Mark 是 搭载一次 Young GC
Root Region Scan 否 扫描存活 Region 引用
Concurrent Mark 否 遍历对象图
Remark 是 处理剩余引用
Cleanup 部分 统计和筛选 Region
Mixed GC 是 分批回收 Region

17.5 Region 与大对象

Region 大小通常为 1MB 到 32MB 的 2 的幂,由堆大小自动决定,也可显式设置:

-XX:G1HeapRegionSize=4m

大对象:

对象 >= 一半 Region
  -> Humongous Region

连续大对象会占用多个连续 Region。

风险:

  1. 大数组;
  2. 大字符串;
  3. 大查询结果;
  4. 大报文;
  5. 批量集合。

处理:

  1. 分页;
  2. 流式处理;
  3. 拆批;
  4. 限制响应大小;
  5. 观察 humongous 指标。

17.6 转移失败

常见概念:

Evacuation Failure / to-space exhausted

含义:回收时没有足够空间接收转移对象。

常见原因:

  1. 堆太小;
  2. 存活对象过多;
  3. 大对象过多;
  4. 并发标记太晚;
  5. 碎片或 Region 分配失败;
  6. 突发流量。

处理:

  1. 扩堆;
  2. 降低存活对象;
  3. 减少大对象;
  4. 提前并发标记;
  5. 调整停顿目标;
  6. 观察晋升。

17.7 GC 日志

启用:

java -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=10,filesize=50m \
     -XX:+UseG1GC \
     -jar app.jar

关键指标:

指标 含义
Pause Young Young GC 停顿
Pause Mixed Mixed GC 停顿
Concurrent Mark Cycle 并发标记
Humongous allocation 大对象分配
Evacuation Failure 转移失败
Heap before / after 堆变化
Post-GWUnit G1 内部单位,以日志说明为准

用 GCEasy、GCViewer、JDK Mission Control 或自建脚本分析趋势。

17.8 调优流程

1. 采集稳定流量下的 GC 日志
2. 明确 P99 / 吞吐 / CPU 目标
3. 先修复大对象和泄漏
4. 观察分配速率、晋升、并发标记时机
5. 小步调整一个参数
6. 压测对比
7. 固化配置
8. 继续监控

常见调参方向:

问题 方向
Young 频繁 降低分配、调整目标或堆
Mixed 不够快 检查并发标记和 Old 水位
转移失败 扩堆、减少存活和大对象
停顿长 降低目标、减少 Region 工作量
CPU 高 并发线程过多或分配过高

17.9 G1 适用场景

适合:

  1. 大堆;
  2. 多核;
  3. 交互式服务;
  4. 需要平衡吞吐和延迟;
  5. 希望默认方案稳妥。

不一定适合:

  1. 极低停顿需求,可评估 ZGC;
  2. 纯吞吐批处理,可评估 Parallel;
  3. 堆很小且资源紧张;
  4. 分配模式极端大对象;
  5. 无法承受额外内存结构开销。

17.10 生产案例

案例一:Mixed GC 后 Old 仍增长

观察:

jstat -gcutil <pid> 1000

GC 日志显示并发标记完成,但每次 Mixed 回收 Region 数量少。

方向:

  1. MaxGCPauseMillis 过小;
  2. 停顿目标限制回收量;
  3. 大对象占用;
  4. 存活对象高;
  5. Old 水位已接近危险。

处理:调整目标、减少大对象、扩堆、观察。

案例二:导出接口导致 Full GC

观察:

Humongous allocation
Evacuation Failure

处理:

  1. SQL 分页;
  2. 流式写出 CSV / Excel;
  3. 限制导出行数;
  4. 异步任务;
  5. 独立实例承接。

本章小结

G1 用 Region 化布局、并发标记和停顿预测模型实现吞吐与延迟的平衡。它适合大堆多核交互服务,但需要关注分配速率、晋升、大对象和转移失败。调优应先修代码和对象分配,再小步调整参数并用日志验证。

思考题

  1. G1 的 Region 有什么作用?
  2. Mixed GC 与 Young GC 的差异是什么?
  3. 什么是 Humongous Region?
  4. 转移失败的常见原因有哪些?
  5. MaxGCPauseMillis 设得越小越好吗?