IWA
2025-12-05
点 赞
0
热 度
7
评 论
0

Java 性能优化实战 第 4 篇:JVM GC 调优(上)——从 GC 原理到 G1 实战参数解析

GC 调优是 Java 性能优化中 最伪玄学、同时也是最硬核 的一块内容。

只要线上出现:

  • 延迟偶发抖动

  • RT 周期性波峰

  • CPU 100% 但业务不满载

  • 老年代突然爆满

  • Young GC 次数暴增

70% 的可能性,都和 GC 有关。

本篇我们专注于:

  • GC 体系整体框架

  • Java 各类 GC 算法演进

  • 如何读懂 GC 日志

  • 为什么 G1 是线上首选

  • 最常用 G1 调优参数(可直接用于生产)


一、GC 的本质:做两件事

不是“回收垃圾”。
是:

  1. 找到垃圾(标记)

  2. 回收垃圾(清除 / 整理)

难点不是回收,而是 ——
在不影响业务的前提下回收。

关键矛盾:

  • 想要更低延迟 → 需要更频繁回收

  • 想要更高吞吐 → 需要更少暂停时间

所以 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=200

G1 会趋近于满足这个延迟目标。

因此 G1 现在是:

线上服务延迟优先时的默认推荐。


📌 5)ZGC / Shenandoah:近乎零停顿(未来方向)

  • ZGC:多染色指针 + 并发整理

  • Shenandoah:基于 Brooks Pointer

优势:
停顿时间都在 1–2ms 左右,不受堆大小影响。

但:

  • ZGC 在 JDK 17 才真正成熟

  • 对内核、硬件要求较高

  • 对延迟敏感场景特别适用

主流系统仍以 G1 为主。


三、如何读懂 GC 日志?(从玄学到可控)

你必须掌握 3 个核心字段:


1)GC 触发原因(Cause)

例如:

  • Allocation Failure

  • GCLocker Initiated GC

  • Metadata GC Threshold

  • System.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 为何是线上最稳妥的选择?

原因很现实:

  1. 自动分区 + 自动调优

  2. 可设置暂停目标

  3. 并发标记和清理

  4. 默认在 JDK 17/21 中表现最大化

  5. 性能、可控性、可维护性最平衡

比 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

解释重点:

参数

含义

MaxGCPauseMillis

核心指标!直接关系到延迟上限

InitiatingHeapOccupancyPercent

触发并发标记的阈值,默认 45,越低越安全

G1HeapRegionSize

Region 大小,一般 8MB 最均衡

ExplicitGCInvokesConcurrent

避免 System.gc() 导致 FullGC

如果你的堆比较大(比如 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


用键盘敲击出的不只是字符,更是一段段生活的剪影、一个个心底的梦想。希望我的文字能像一束光,在您阅读的瞬间,照亮某个角落,带来一丝温暖与共鸣。

IWA

infp 调停者

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

文章目录

IWA的艺术编程,为您导航全站动态

42 文章数
9 分类数
10 评论数
34标签数
最近评论
IWA

IWA


👍

M丶Rock

M丶Rock


😂

M丶Rock

M丶Rock


感慨了

M丶Rock

M丶Rock


厉害了

M丶Rock

M丶Rock


6666666666666666666

访问统计