Java

G1 GC 深度优化

·16 分钟阅读·6014 字

G1 垃圾收集器原理回顾、核心调优参数、日志解读、常见问题排查

📋 目录

G1 GC 深度优化

面向运维的 G1 GC 调优实战——参数配置、日志分析、问题排查。


一、G1 基础回顾(调优前置认知)

1. 核心设计

G1(Garbage-First)全称优先回收垃圾最多的区域,JDK9 默认,JDK8 线上业务主流选择。

  • 不再划分固定新生代/老年代,把整个堆划分为多个大小相等的 Region(分区),默认分区大小 1MB~32MB,JVM 自动计算。
  • Region 角色:Eden、Survivor、Old、Humongous(大对象分区,存放 > 分区大小一半的对象)。
  • 目标:可控停顿时间,兼顾吞吐量与低延迟,适合堆内存较大(>4G)、对响应耗时敏感的在线服务。

2. G1 四大 GC 阶段

  1. Young GC:仅回收 Eden/Survivor 分区,常规新生代回收,频率高、停顿短。
  2. Mixed GC:G1 特色,回收全部新生代 + 部分垃圾最多的老年代分区,替代传统 Full GC,是线上最核心的回收阶段。
  3. Full GC:串行全堆回收,极度耗时、应用卡顿严重,属于异常场景,调优核心目标就是杜绝主动触发 Full GC
  4. Humongous GC:专门清理大对象分区,大对象过多会触发,易引发卡顿。

3. 核心运行流程

常规链路:Eden 满 → Young GC当老年代占比达到阈值 → 触发 并发标记周期(扫描全堆存活对象,并发执行不阻塞业务)
标记完成后 → 执行 Mixed GC,分批回收新生代+高垃圾老年代分区。


二、G1 核心调优参数(按优先级排序)

分为 停顿控制、内存阈值、分区/大对象、辅助监控 四大类,附生产推荐值、作用、适用场景。

(一)核心停顿时间控制(最高优先级)

1. -XX:MaxGCPauseMillis

  • 作用:期望的最大GC停顿时间(目标值,非强制),G1 会尽力贴合该阈值规划回收分区数量。

  • 单位:毫秒

  • 生产推荐:

  • 普通业务接口:200(主流标配)

  • 高并发低延迟接口(支付、秒杀):100

  • 后台非实时服务:300~500

  • 注意:

  • 设置过小:G1 每次只回收少量分区,回收效率低,老年代持续上涨,频繁触发 Mixed GC,最终反而引发长时间 Full GC。

  • 设置过大:单次GC停顿变长,接口超时、RT 飙升。

# 示例:目标最大停顿 200ms
-XX:MaxGCPauseMillis=200

2. -XX:G1HeapRegionSize

  • 作用:手动指定 Region 分区大小,取值固定:1/2/4/8/16/32MB,JVM 默认自动计算。
  • 规则:大对象(Humongous Object)定义:对象大小 > Region 容量的 1/2,会单独分配到 Humongous 分区。
  • 生产建议:
  1. 绝大多数场景不手动设置,交给 JVM 自动适配;
  2. 业务存在大量 10MB+ 大对象时,调大分区(如 16m),减少 Humongous 分区数量;
  3. 分区过小 → 大对象泛滥,Humongous GC 增多。
# 手动指定分区 16MB(大对象多场景使用)
-XX:G1HeapRegionSize=16m

(二)并发标记 & Mixed GC 阈值(控制 GC 触发时机)

1. -XX:InitiatingHeapOccupancyPercent 简称 IHOP

  • 作用:触发并发标记周期的堆内存占用阈值
    当整个堆的已用占比达到该值,G1 开始执行并发标记(扫描存活对象)。
  • 默认值:45
  • 调优规则:
  1. 默认 45 适合 8~16G 堆,直接沿用
  2. 堆 >16G / 对象晋升速度快:调低至 35~40,提前启动标记,预留充足回收时间;
  3. 堆 <4G / 对象生命周期极短:可小幅调高至 50,减少不必要的标记;
  4. 禁忌:不要低于 30,标记过于频繁,消耗CPU。
# 大堆内存,提前触发并发标记
-XX:InitiatingHeapOccupancyPercent=40

2. -XX:G1MixedGCLiveThresholdPercent

  • 作用:Mixed GC 筛选老年代分区的阈值。分区内存活对象占比低于该值,才会被纳入 Mixed GC 回收列表。

  • 默认:85

  • 解读:分区垃圾越多(存活越少),越优先回收。

  • 调优:

  • 线上卡顿、Mixed GC 回收效率低:调低到 70~80,多回收老年代分区;

  • 停顿超时:调高,减少单次回收分区数量。

3. -XX:G1OldCSetRegionThresholdPercent

  • 作用:Mixed GC 一轮最多回收的老年代分区占整个堆分区的比例,限制单次回收范围,控制停顿。
  • 默认:10
  • 场景:老年代增长快、一轮回收不完 → 小幅调高(12~15);停顿超标 → 降低。

4. 混合GC轮数控制

  • -XX:G1MixedGCCountTarget:并发标记结束后,执行多少轮 Mixed GC 来消化老年代垃圾。
    默认 8

  • 调优:

  • 老年代上涨快:调高到 10~12,多轮分批回收;

  • 频繁 Mixed GC 导致CPU高:调低。


(三)大对象(Humongous)专项优化(高频坑点)

大对象是 G1 卡顿、Full GC 的常见诱因:

  1. 大对象单独占用分区,无法被合并,容易产生分区碎片;
  2. Humongous 分区回收逻辑特殊,回收效率低;
  3. 大量临时大对象,会直接填满堆,触发 Full GC。

关键参数

  1. -XX:+G1HumongousObjectReclamation
    JDK8u102+ 支持,默认开启,允许在 Young GC 时回收无引用的大对象。不要关闭
  2. 优化手段(参数+业务配合)
  • 方案1:调大 G1HeapRegionSize,降低大对象判定数量;
  • 方案2:业务侧优化:批量查询拆分、避免一次性加载超大集合/文件;
  • 方案3:观察 GC 日志,统计 Humongous 分配频率,判断是否存在代码问题。

(四)新生代相关参数(G1 不推荐强制固定新生代)

传统收集器用 -Xmn 固定新生代大小,G1 不建议强行指定

  • G1 会根据停顿目标,动态调整 Eden/Survivor 分区数量,自适应业务流量。
  • 强行 -Xmn 会破坏 G1 自适应能力,失去低延迟优势。

补充参数(按需使用)

  1. -XX:SurvivorRatio:Eden 和单个 Survivor 比例,默认 8,绝大多数场景保持默认。
  2. -XX:MaxTenuringThreshold:对象晋升老年代年龄阈值,默认15。
  • 短期临时对象多:保持15,尽量留在新生代;
  • 对象存活久、频繁晋升:可降至 8~10,减少新生代压力。
# 调整对象晋升年龄
-XX:MaxTenuringThreshold=12

(五)安全兜底 & 辅助参数

1. 并发线程数

# 并发标记、并发回收使用的线程数,默认 CPU核心数/4
-XX:ConcGCThreads=4
# 停止世界(STW)阶段工作线程数,默认 CPU核心数
-XX:ParallelGCThreads=8
  • 规则:

  • 8核及以上服务器:ParallelGCThreads 设为核心数,ConcGCThreads 设为核心数的 1/4~1/2;

  • 核心数少(4核及以下):保持默认,避免线程竞争。

2. 并发标记兜底(预防并发失败)

-XX:+G1ResolveZeroDuringConcurrentMark

规避并发标记期间堆被快速占满,导致并发标记失败,进而触发串行 Full GC。线上建议开启。


三、分场景标准配置(直接复制落地)

场景1:通用在线业务(8C16G 服务器,JDK8,最常用)

目标:停顿200ms,平衡吞吐量与延迟

# 基础内存
-Xms9g -Xmx9g
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
-Xss256k

# G1 核心配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=2

# 辅助兜底
-XX:MaxTenuringThreshold=15
-XX:+G1ResolveZeroDuringConcurrentMark

# 日志&故障兜底(必加)
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/opt/logs/gc.log
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/logs/heap.hprof

场景2:高并发低延迟服务(支付/网关,16C32G)

目标:更低停顿,提前触发标记,快速回收老年代

-Xms18g -Xmx18g
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
-Xss256k

-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:InitiatingHeapOccupancyPercent=38  # 提前启动并发标记
-XX:G1MixedGCCountTarget=10
-XX:ParallelGCThreads=16
-XX:ConcGCThreads=4
-XX:G1HeapRegionSize=16m  # 预防大对象

场景3:后台任务/报表服务(对延迟不敏感,16G堆)

目标:放宽停顿,提升吞吐量

-Xms9g -Xmx9g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=400
-XX:InitiatingHeapOccupancyPercent=50

四、G1 调优判断指标(线上监控+GC日志分析)

结合 jstat + GC 日志,判断当前配置是否合理,按以下顺序排查。

1. 基础指标(jstat -gc pid 1000)

  1. YGC/YGCT
  • 新生代GC频繁(每秒多次):大概率新生代分配压力大,检查是否大量临时对象、大对象;
  • 单次YGC停顿接近 MaxGCPauseMillis:调大停顿目标,或增加GC线程。
  1. FGC/FGCT
  • 出现 Full GC = 严重异常,立刻排查:
  1. 并发标记失败(堆增长过快,IHOP 设置过高);

  2. 大对象耗尽分区;

  3. 内存泄漏;

  4. 堆内存过小。

  5. 老年代使用率:平稳缓慢上涨,Mixed GC 后明显下降 → 正常;
    老年代持续单向上涨,GC 回收效果差 → 内存泄漏 或 Mixed GC 回收力度不足。

2. GC 日志关键字解读

  1. Concurrent Cycle:并发标记周期,正常应有规律执行;
    连续多次并发标记 → 堆压力大,调低 IHOP。
  2. Mixed GC:混合GC,正常主力回收;
    Mixed GC 频繁、回收量少 → 调高 G1MixedGCLiveThresholdPercent,多回收老年代。
  3. Humongous Allocation:大对象分配,频繁出现 → 优化分区大小+业务代码。
  4. Full GC (Ergonomics) / Full GC (Concurrent Mark Failure)高危,并发标记失败,优先调低 IHOP、加大堆。

五、G1 线上典型问题 & 解决方案(运维高频踩坑)

问题1:并发标记失败,触发 Full GC

  • 现象:GC 日志出现 Concurrent Mark Failure,紧跟着串行 Full GC,应用卡顿。
  • 根因:对象晋升速度 > 标记+回收速度,堆在标记阶段被占满。
  • 解决方案:
  1. 调低 InitiatingHeapOccupancyPercent(45 → 35~40),提前启动标记;
  2. 适当加大堆内存;
  3. 检查是否有瞬时流量峰值,做限流。

问题2:大量大对象,Humongous GC 频繁

  • 现象:GC 日志频繁打印 Humongous 相关日志,单次GC停顿波动大。
  • 解决方案:
  1. 调大 G1HeapRegionSize
  2. 业务拆分超大集合、IO 缓冲区;
  3. 排查代码中一次性创建的大数组。

问题3:Mixed GC 执行频繁,CPU 占用高

  • 现象:CPU 持续偏高,Mixed GC 一轮接一轮。
  • 根因:单次Mixed GC回收分区太少,老年代垃圾清不完。
  • 解决方案:
  1. 调低 G1MixedGCLiveThresholdPercent,扩大回收范围;
  2. 调高 G1MixedGCCountTarget,增加混合GC轮数;
  3. 轻微调高停顿时间 MaxGCPauseMillis

问题4:强行设置 -Xmn 后,G1 性能变差

  • 根因:固定新生代,破坏 G1 动态自适应特性。
  • 解决方案:删除 -Xmn,交给 G1 自动调整新生代大小。

问题5:GC 停顿远超配置的 MaxGCPauseMillis

  • 根因:单次回收分区过多、系统CPU/IO资源竞争、STW 线程阻塞。
  • 解决方案:
  1. 小幅降低 G1OldCSetRegionThresholdPercent,减少单次回收分区;
  2. 检查服务器资源,避免多服务抢占CPU;
  3. 排查是否有同步锁、JNI 调用阻塞STW。

六、G1 调优整体流程(标准运维步骤)

  1. 基线配置:套用对应服务器规格的标准模板,不盲目改参数;
  2. 观测指标:运行1~2天,监控 YGC、Mixed GC、Full GC、停顿时间、堆使用率;
  3. 定向调优
  • 出现 Full GC → 优先调整 IHOP、加大堆;
  • 停顿超标 → 放宽 MaxGCPauseMillis 或减少单次回收分区;
  • 大对象多 → 调整 Region 大小;
  • 老年代回收不彻底 → 强化 Mixed GC 回收力度;
  1. 回归验证:调参后持续观测,对比GC频率、停顿、CPU 变化;
  2. 固化配置:稳定后写入启动脚本,归档GC日志用于后续复盘。

七、补充:JDK17+ G1 新特性简述

JDK17 对 G1 做了大量优化:

  1. 增强并发标记、降低标记阶段CPU消耗;
  2. 优化大对象分配与回收;
  3. 默认参数更激进,低延迟表现更好。
    原有调优逻辑完全兼容,线上可直接沿用上述参数。


关联文档