GC 日志怎么看?
更新时间:2026-09-20。问题驱动短答页。GC 日志是判断堆大小是否合理、Young/Full GC 频率与停顿、是否内存泄漏的一手证据。配合实时监控见 jstat 与 GC 调优,频繁 Full GC 见 Full GC 频繁怎么排查。
一句话回答
先开启并拿到 GC 日志(JDK8 用 -XX:+PrintGCDetails,JDK9+ 用 -Xlog:gc*),再逐行看三件事:是什么 GC(Young/Mixed/Full)、为什么触发(Cause)、各代回收前后占用和 Pause 停顿多久;用频率和「回收后老年代能否降下来」判断是堆小、晋升快还是内存泄漏。
一、开启参数
bash
# JDK 8
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -Xloggc:/var/log/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M
# JDK 9+(统一日志框架 -Xlog)
-Xlog:gc*,gc+age=trace,safepoint:file=/var/log/gc-%t.log:time,uptime,level,tags:filecount=5,filesize=20m
# 建议同时开启 OOM 自动 dump
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/%t 会带时间戳;:time,uptime,level,tags 控制输出字段。
二、一行 G1 日志怎么读
JDK9+ G1 典型两行(标记并发阶段 + 暂停):
bash
[3.125s] GC pause Young (G1 Evacuation Pause) 512M->120M(1024M) 12.345ms| 片段 | 含义 |
|---|---|
3.125s | JVM 启动后经过时间 |
GC pause | 一次 STW 暂停 |
Young / Mixed / Full | GC 类型 |
(G1 Evacuation Pause) / Cause | 触发原因(Allocation Loaded、Metadata GC Threshold、System.gc() 等) |
512M->120M | 回收前堆使用 -> 回收后使用 |
(1024M) | 当前堆总容量 |
12.345ms | 本次暂停时长(关注 P99/最大值) |
JDK8 的 [PSYoungGen: 204800K->17408K(234496K)] 312000K->150000K(502784K) 同理:方括号内是新生代变化,外面是整个堆前后值与总容量。
三、判断要点
- Young GC 频率与单次耗时:每分钟几次、每次几 ms~几十 ms 通常健康;单次越来越长要警惕堆过大或晋升。
- Full GC 频率:偶发可接受,周期性出现且停顿长就是问题,按 Cause 分流(见下)。
- 回收后老年代/堆是否下降:Full GC 后仍高位并逐次抬升 → 倾向内存泄漏;能降但很快又满 → 堆小/晋升过快。
- Pause 最大值/P99:直接影响接口 RT 毛刺,比平均停顿更重要。
- 并发标记是否来不及(G1
to-space exhausted、Humongous 分配):调 Region/堆或提前并发周期。
按 Cause 分流
| Cause | 方向 |
|---|---|
Allocation Failure(普通 Young) | 常态,关注频率即可 |
Metadata GC Threshold | Metaspace 不足,调 MaxMetaspaceSize、查类加载泄漏 |
System.gc() | 显式调用/RMI/NIO,加 -XX:+ExplicitGCInvokesConcurrent |
G1 Humongous Allocation | 大对象多,调 Region 大小/排查大数组 |
to-space exhausted | G1 疏散空间不足,扩堆或调并发线程/IHOP |
四、配合 jstat 与工具
bash
jstat -gcutil <pid> 1000 10 # S0/S1/E/O/M 使用率 + YGC/YGCT/FGC/FGCTFGC列涨速、FGCT累计停顿最直观。- 离线分析把日志拖进 GCEasy / GCViewer / JDK Mission Control(JMC),自动出吞吐、停顿分布、各代曲线。
- 想进一步定位对象用 jmap 堆转储 + MAT。
常见坑
- 只看 GC 次数不看停顿:G1 Young GC 多很正常,关键是 Pause 时长和 Full GC。
- 生产没开 GC 日志:出问题时没有历史证据;现代 JDK 开 GC 日志开销很小,建议常驻。
- 把 JDK9+ 参数照抄成 PrintGCDetails:已废弃不生效,统一用
-Xlog:gc*。 - 只看平均耗时:尾停顿(max/P99)才对应线上 RT 尖刺。
- 忽略 Cause:同是 Full GC,System.gc 与泄漏的处置完全不同。
总结
看 GC 日志先按 JDK 版本开启(JDK8 -XX:+PrintGCDetails -Xloggc,JDK9+ -Xlog:gc:file),再从每行读 GC 类型、触发 Cause、各代与整堆回收前后容量、Pause 停顿;健康度看 Young GC 频率与单次时长、Full GC 是否周期性出现、以及 Full GC 后老年代能否降下来——降不下来偏泄漏需导堆 MAT、能降但很快满偏堆小或晋升快,再用 jstat -gcutil 看 FGC/FGCT、GCEasy/JMC 出停顿分布,关注 P99 与最大停顿而非平均值。*