这是「一文拿下 GC」系列第一篇。GC 调优是 Java 后端的必备技能——掌握日志参数、工具、参考值三件套,线上问题排查就有了第一道防线。
一、配置 GC 日志参数
排查 GC 问题的第一步是拿到 GC 日志。HotSpot 提供了一系列参数把 GC 行为输出到文件或控制台:
-XX:+PrintGCDetails # GC 时打印详细日志
-XX:+PrintGCTimeStamps # 打印 GC 时间戳(相对 JVM 启动时间)
-XX:+PrintGCDateStamps # 打印 GC 日期(绝对时间戳)
-Xloggc:./logs/gc.log # 把 GC 日志输出到指定文件
四个参数通常一起配置——才能拿到「时间 + 日期 + 详细 + 文件」完整的 GC 日志。
2017 年冷知识:当年
PrintGCDetails是 HotSpot 最常用的 GC 日志开关,G1下还会额外打 region 信息。生产环境强烈建议输出到独立日志文件,方便后续分析。
二、GC 日志长什么样
直接看一段实际的 GC 日志(Parallel Scavenge + ParOldGen):
[PSYoungGen: 36857K->5114K(36864K)] 41977K->14380K(121856K), 0.0274265 secs] [Times: user=0.07 sys=0.01, real=0.02 secs]
字段解读:
[PSYoungGen: 36857K->5114K(36864K)]:young 区 GC 前后大小 + young 区总容量41977K->14380K(121856K):整个堆 GC 前后大小 + 堆总容量0.0274265 secs:本次 GC 耗时[Times: user=0.07 sys=0.01, real=0.02 secs]:CPU 时间和墙钟时间(real < user 是多核并行 GC 的典型特征)
三、GC 日志分析工具
3.1 在线工具
- gceasy(gceasy.io):把 GC 日志拖进去,自动生成关键指标 + 趋势图 + 异常检测
- GCViewer(本地工具):开源的 GC 日志分析桌面程序
3.2 命令行工具
# 查看 JVM 进程启动时长(看 JVM 重启时间点)
ps -p <pid> -o etime
# GC 统计:YGC(young GC 次数)、YGCT(young GC 总时长)、FGC(full GC 次数)、FGCT(full GC 总时长)
jstat -gcutil <pid>
通过 jstat 的输出可以算出:
- Young GC 频率 = YGC / JVM 运行时长
- Young GC 平均时长 = YGCT / YGC
- Full GC 频率 = FGC / JVM 运行时长
2017 年冷知识:当年 gceasy 刚上线不久,是排查 GC 问题的神器——比手算
jstat输出直观很多。后来(2018+)被各类 APM 工具集成,2020 年后基本是自动化采集 + AI 异常检测了。

四、参考值
调优的核心目标是「该 GC 才 GC,不该 GC 别频繁」。基于生产经验:
4.1 Young GC
- 建议频率 5s/次,3~6s/次都是比较合理的值
- 如果频率远高于 5s/次(比如 20s/次),说明 JVM 相当空闲,可以考虑缩容
- 如果频率远低于 5s/次(比如 1s/次),可扩大 young 区大小(
-Xmn)
4.2 Full GC
- Full GC 频率不超过 1 次/天
- 每次 Young GC 时长不超过 20ms(百毫秒级 Young GC 已经严重影响响应时间)
- Full GC 尽可能完全杜绝(Full GC 的 STW 经常几百毫秒甚至秒级)
4.3 Old GC
- Old GC 频率不超 1 次/天
- 如果超出,检查 Young 和 Old 的比例,尽量留 60% 以上堆给 Old
- 通过
jstat检查每次 Young GC 后晋升到 Old 的对象占比——如果每次 Young GC 后 Old 区涨好几个百分点甚至上 10 个百分点,说明有大对象 - 大对象排查:使用
jmapdump 内存映像,用 MAT(Memory Analyzer Tool)分析引用链 - 如果不停 Old GC 且 Old 区降不下来,使用
jmap -histo <pid> | head -n20查看 top 20 对象分布——除了[B(byte[])、[C(char[])这些基本类型数组,分析其他占比较大的对象
2017 年冷知识:当年还没有 G1 普及,生产环境多半在用 CMS——Full GC 频率就是 CMS 调优的核心 KPI。今天(2026 年)G1 是 OpenJDK 8+ 默认收集器,参考值有所变化,但「Full GC 必须避免」这一点没变。
五、查看当前使用的垃圾收集器
# 查看 JVM 默认的垃圾收集器
java -XX:+PrintCommandLineFlags -version
# 查看指定 JVM 进程是否开启了某种收集器
jinfo -flag UseParallelGC <pid> # 是否使用 Parallel GC
jinfo -flag UseConcMarkSweepGC <pid> # 是否使用 CMS
jinfo -flag UseParNewGC <pid> # 是否使用 ParNew
jinfo -flag <参数> <pid> 返回 -XX:+<参数> 表示开启,-XX:-<参数> 表示关闭。
小结
GC 调优不是「调参数让 GC 不发生」——GC 该发生还得发生,调的是「频率 + 时长 + 时机」。这一篇建立基本的「日志 + 工具 + 参考值」三件套,下一篇开始讲「GC 怎么判断对象该死」。