GC日志分析
GC 日志是分析 JVM 内存和停顿问题的第一手资料。没有 GC 日志,很多调优只能靠猜。
# 1. 开启 GC 日志
JDK 9 之后使用统一日志:
-Xlog:gc*:file=/data/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100m
JDK 8 常见写法:
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/data/logs/gc.log
生产环境建议长期保留 GC 日志,并开启滚动,避免单个日志文件过大。
# 2. 重点看什么
分析 GC 日志重点关注:
- GC 类型。
- GC 触发原因。
- 回收前后堆变化。
- 新生代和老年代变化。
- 单次停顿时间。
- Full GC 次数和原因。
- 是否存在晋升失败、分配失败。
- 老年代是否持续增长。
# 3. Young GC 分析
Young GC 主要看:
- 频率是否过高。
- 每次停顿是否可接受。
- Eden 回收后是否释放明显。
- Survivor 是否过小。
- 是否大量对象晋升到老年代。
如果 Young GC 很频繁,可能是:
- 对象创建速度过快。
- 新生代过小。
- 短生命周期大对象过多。
# 4. Full GC 分析
Full GC 需要重点关注。
常见原因:
- 老年代空间不足。
- 元空间不足。
- 显式调用
System.gc()。 - 大对象分配失败。
- G1 并发回收失败退化。
Full GC 频繁通常说明 JVM 已经处于不健康状态。
# 5. 老年代趋势
如果每次 GC 后老年代占用仍持续上升,就要怀疑:
- 内存泄漏。
- 缓存增长。
- 长生命周期对象过多。
- 晋升速度大于老年代回收速度。
此时需要结合堆转储分析对象引用链。
# 6. 常用分析工具
可以使用:
- GCViewer。
- GCeasy。
- JDK Mission Control。
- 各类 APM 平台。
- 自研日志解析脚本。
工具可以帮助统计,但最终仍要结合业务时间线和指标判断。
# 7. GC 日志和业务指标联动
不要孤立看 GC 日志。应该对齐:
- 请求量。
- 响应时间。
- CPU。
- 内存。
- 线程数。
- 错误率。
- 发布时间点。
例如 Full GC 发生在流量高峰、批任务启动或发布后,含义完全不同。
# 8. 阶段小结
GC 日志分析的核心是看趋势和影响:对象分配是否过快、老年代是否增长、停顿是否影响业务、Full GC 是否异常。调优必须基于日志,而不是凭经验直接改参数。
# 9. GC 日志分析模板
分析一份 GC 日志时,可以按下面模板记录:
| 项目 | 记录内容 |
|---|---|
| JDK 版本 | 例如 JDK 17 |
| GC 类型 | G1 / ZGC / Parallel |
| 堆大小 | Xms、Xmx |
| 业务时间段 | 高峰、低峰、发布后 |
| Young GC 频率 | 每分钟多少次 |
| Young GC 平均停顿 | 平均、P95、最大 |
| Full GC 次数 | 是否出现,原因是什么 |
| 老年代趋势 | GC 后是否持续增长 |
| 大对象信号 | Humongous、promotion failure 等 |
| 业务影响 | 是否和 RT 抖动重合 |
这样能避免只截取一行日志就下结论。
# 10. G1 日志读法示例
示意日志:
[info][gc] GC(12) Pause Young (Normal) (G1 Evacuation Pause) 512M->128M(1024M) 12.3ms
可读出:
GC(12):第 12 次 GC。Pause Young:年轻代停顿。G1 Evacuation Pause:G1 对象转移暂停。512M->128M(1024M):回收前 512M,回收后 128M,总堆 1024M。12.3ms:停顿时间。
不要只看 12.3ms,还要看回收后水位是否持续上升。
# 11. 延迟对齐图
时间轴:
10:00:00 RT 20ms
10:00:01 RT 25ms
10:00:02 GC Pause 800ms
10:00:02 RT 850ms
10:00:03 RT 22ms
如果 RT 尖刺和 GC 停顿高度重合,可以判断 GC 对延迟有明显贡献。
如果 RT 高但没有 GC,则要看锁、IO、数据库、线程池等其他因素。
# 12. 常见异常信号
| 信号 | 可能原因 |
|---|---|
| Young GC 很频繁 | 分配速率高,新生代小 |
| GC 后老年代持续增长 | 泄漏或长生命周期对象多 |
| Full GC 后下降很少 | 大量对象仍可达 |
| Humongous 很多 | 大数组、大字符串、大对象 |
| To-space exhausted | G1 转移空间不足 |
| Metadata GC Threshold | 元空间压力 |
# 13. Tips 快问快答
Q:只看一次 GC 日志够吗?
A:不够。要看一段时间内的趋势,最好覆盖高峰和异常时间点。
Q:GC 日志里的总堆下降,说明没泄漏吗?
A:不一定。要看回收后水位是否长期上升,以及对象引用链。
Q:Full GC 后内存下降很多是不是好事?
A:说明可回收对象多,但频繁 Full GC 仍然说明内存压力或分配模式有问题。
Q:GC 日志能定位到具体哪行代码泄漏吗?
A:不能。GC 日志能发现趋势和触发原因,具体对象引用链要靠 heap dump 等工具。