GC 调优是 Java 性能优化中 最伪玄学、同时也是最硬核 的一块内容。
只要线上出现:
延迟偶发抖动
RT 周期性波峰
CPU 100% 但业务不满载
老年代突然爆满
Young GC 次数暴增
70% 的可能性,都和 GC 有关。
本篇我们专注于:
GC 体系整体框架
Java 各类 GC 算法演进
如何读懂 GC 日志
为什么 G1 是线上首选
最常用 G1 调优参数(可直接用于生产)
一、GC 的本质:做两件事
不是“回收垃圾”。
是:
找到垃圾(标记)
回收垃圾(清除 / 整理)
难点不是回收,而是 ——
在不影响业务的前提下回收。
关键矛盾:
想要更低延迟 → 需要更频繁回收
想要更高吞吐 → 需要更少暂停时间
所以 GC 的目标一直在 延迟 和 吞吐量 之间权衡。
二、GC 发展史(理解这段就理解了为什么必须用 G1)
📌 1)Serial:单线程 + Stop-The-World
标记、清理都用单线程
停顿长
适合小内存、小应用
现在没人用了。
📌 2)ParNew:Young 区并行 GC
多线程处理年轻代
和 CMS 搭配使用
CMS 时代遗物。
📌 3)CMS(Concurrent Mark Sweep):低延迟标志时代
优点:
大部分阶段并发,不阻塞业务
缺点致命:
无法整理内存(导致碎片)
有 “浮动垃圾”
Full GC 几乎不可控、停顿高达几秒
JDK 9 开始被标记为 Deprecated,JDK 14 被移除。
📌 4)G1:Garbage First(现代 GC 的主流)
G1 的两大突破:
✔ 1. 不再有固定年老代 / 年轻代分区
改为均匀小 Region(1~32MB),可灵活扩 shrink。
✔ 2. 并发标记 + 并行回收
回收最“脏”的 Region → 吞吐高
✔ 3. 用户可指定最大暂停时间(Pause Target)
如:
-XX:MaxGCPauseMillis=200G1 会趋近于满足这个延迟目标。
因此 G1 现在是:
线上服务延迟优先时的默认推荐。
📌 5)ZGC / Shenandoah:近乎零停顿(未来方向)
ZGC:多染色指针 + 并发整理
Shenandoah:基于 Brooks Pointer
优势:
停顿时间都在 1–2ms 左右,不受堆大小影响。
但:
ZGC 在 JDK 17 才真正成熟
对内核、硬件要求较高
对延迟敏感场景特别适用
主流系统仍以 G1 为主。
三、如何读懂 GC 日志?(从玄学到可控)
你必须掌握 3 个核心字段:
1)GC 触发原因(Cause)
例如:
Allocation FailureGCLocker Initiated GCMetadata GC ThresholdSystem.gc()
这些能帮你定位是“内存泄漏”还是“压测数据暴涨”。
2)GC 前后内存变化
格式类似:
[GC pause (G1 Evacuation Pause) (young)
512M->128M(1024M), 0.0156789 secs]解读:
Young GC
回收前 512MB
回收后 128MB
堆总共 1024MB
暂停时间 15ms
你需要关注的是:
回收效率够不够
Young 区是否写满
回收时间是否超出 SLA
3)GC 暂停时间(pause time)
RT 波动大多数来自 pause。
一般要求:
Web 服务:5–50ms
金融/交易类系统:<20ms
延迟敏感系统:1–5ms(ZGC/SH)
如果 GC 日志里存在:
300ms、600ms、1s 级别停顿
意味着必须调优。
四、G1 为何是线上最稳妥的选择?
原因很现实:
自动分区 + 自动调优
可设置暂停目标
并发标记和清理
默认在 JDK 17/21 中表现最大化
性能、可控性、可维护性最平衡
比 CMS 更稳
比 ZGC 更成熟兼容
G1 是综合评分最高的 GC。
五、G1 最核心的调优参数(生产可直接复制)
下面是“最小可用调优集”,适合 90% 的线上服务:
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
-XX:ParallelGCThreads=CPU核数
-XX:ConcGCThreads=CPU核数/4
-XX:G1HeapRegionSize=8m
-XX:MinMetaspaceFreeRatio=50
-XX:MaxMetaspaceFreeRatio=80
-XX:+ExplicitGCInvokesConcurrent解释重点:
如果你的堆比较大(比如 8G+),将:
-XX:InitiatingHeapOccupancyPercent=30能显著降低 FullGC 风险。
六、线上 90% 的 GC 问题来源于三个坑
坑 1:Young 区太小(YGC 超频)
表现:
Young GC 每秒十几次甚至几十次
RT 波动明显
解决:
-XX:G1NewSizePercent=20
-XX:G1MaxNewSizePercent=60坑 2:Old 区填满导致 Mixed GC 来不及回收
表现:
Old 区增长快
Mixed GC 频率不足
最终触发 Full GC
解决:
-XX:InitiatingHeapOccupancyPercent=30坑 3:GC 耗时集中在 Evacuation(复制阶段)
可能原因:
对象太大
Region 不够
并发线程不足
解决:
-XX:+UseStringDeduplication
-XX:ParallelGCThreads=<CPU 核数>下一篇预告(第 5 篇)
第 5 篇我们讲:
《GC 调优(下)》— GC 事故排查实战 + GC 日志可视化分析
内容包括:
如何定位“内存泄漏”
如何判断是 GC 问题还是业务问题
常见 GC 事故(CPU 高 / RT 抖动)排查流程
GC Easy / GCeasy / GCViewer 实战
不同堆大小的最佳调优模版
如何选择 G1 / ZGC / Shenandoah
默认评论
Halo系统提供的评论