要真正用好gc日志,关键在于参数配置完整且可读:jdk 8及以下用-xx:+printgcdetails等组合,jdk 9+必须用-xlog语法;需重点关注young gc频率与耗时、full gc次数、stw占比、老年代增长趋势四项核心指标。

要真正用好 GC 日志,关键不是堆参数,而是让日志既完整又可读——参数配错,等于没开;日志缺关键字段,等于盲调。
生产环境推荐的 GC 日志参数组合
不同 JDK 版本写法差异大,不能混用。以下按主流版本分列,可直接复制上线:
-
JDK 8 及以下(CMS/Parallel):
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime
-XX:+PrintTenuringDistribution -Xloggc:/data/logs/gc-%t.log
-XX:GCLogFileSize=100M -XX:NumberOfGCLogFiles=10 -XX:+UseGCLogFileRotation -
JDK 9+(G1/ZGC/Shenandoah):
-Xlog:gc*:file=/data/logs/gc-%t.log:time,uptime,level,tags
-Xlog:gc+heap=debug -Xlog:gc+safepoint=trace
注意:-XX:+PrintGC 只输出一行摘要,信息太少;-XX:+PrintGCDetails 才是分析基础。JDK 9 起废弃旧参数,必须用 -Xlog 语法,否则不生效。
GC 日志里必须盯住的四个数字
别被满屏字段吓住,真正决定系统是否健康,就看这四项:
- Young GC 频率与单次耗时:每秒超过 2 次或单次 > 50ms,说明 Eden 太小、对象生命周期异常,或 Survivor 区太小导致提前晋升。
-
Full GC 次数:线上服务一天内出现 1 次 Full GC 就需排查;若频繁触发(如每小时多次),大概率是老年代内存泄漏、元空间溢出,或代码中误调
System.gc()。 - STW 总暂停时间占比:GC 导致的停顿总时长 ÷ 应用运行总时长。超过 2% 就会影响响应稳定性,接口超时风险陡增。
- 老年代内存增长趋势:匀速缓慢上升属正常;若某段时间突增且不回落,重点查大对象分配、缓存未清理、线程局部变量堆积等问题。
典型日志行逐段拆解(以 JDK 8 Parallel GC 为例)
看懂一行,就能读懂全部。例如:
2026-07-05T14:22:18.341+0800: 12345.678: [GC (Allocation Failure) [PSYoungGen: 102400K->2048K(153600K)] 122880K->22528K(524288K), 0.0123456 secs]- 2026-07-05T14:22:18.341+0800:绝对时间戳,用于对齐业务错误日志
- 12345.678:JVM 启动后经过的秒数,方便统计 GC 间隔
- (Allocation Failure):YGC 触发原因,最常见,说明 Eden 填满
- PSYoungGen: 102400K→2048K(153600K):新生代回收前/后使用量,括号为总容量;差值≈被回收对象大小
- 122880K→22528K(524288K):整个堆使用量变化,能看出有多少对象晋升到老年代(122880−102400 = 20480K 进入老年代)
- 0.0123456 secs:STW 时间,即用户线程完全暂停时长
别踩这些日志配置坑
- 只加
-XX:+PrintGC就以为开了详细日志——它连 Survivor 使用率都不显示,根本没法判断晋升行为。 - 漏配
-XX:+PrintGCDateStamps——没有绝对时间戳,故障发生时无法和监控指标、业务日志对齐。 - 没设日志滚动(
-XX:+UseGCLogFileRotation等)——GC 日志暴涨,磁盘打满导致服务宕机。 - JDK 11+ 还用
-XX:+PrintGCDetails——参数已失效,日志完全不输出,还以为“没 GC”。
不复杂但容易忽略











