GC 日志分析
面试提问
"线上服务出现频繁 GC,你如何通过 GC 日志定位问题?"
核心概念
GC 日志是 JVM 调优的重要依据,记录了每次 GC 的详细信息,包括:
- GC 类型(Young GC / Full GC)
- GC 原因
- 各区域内存变化
- GC 耗时
通过分析 GC 日志,可以定位内存问题、优化 JVM 参数。
开启 GC 日志
JDK8 及之前
-XX:+PrintGCDetails # 打印详细 GC 信息
-XX:+PrintGCDateStamps # 打印日期时间戳
-XX:+PrintGCTimeStamps # 打印 JVM 启动后的相对时间
-XX:+PrintGCCause # 打印 GC 原因
-Xloggc:gc.log # GC 日志输出文件
-XX:+UseGCLogFileRotation # 日志轮转
-XX:NumberOfGCLogFiles=5 # 日志文件数量
-XX:GCLogFileSize=10M # 单个日志文件大小JDK9+(统一日志框架)
# 基本格式:-Xlog:what:output:decorators:output-options
-Xlog:gc*:file=gc.log:time,level,tags:filecount=5,filesize=10m常用配置:
# 打印所有 GC 相关日志
-Xlog:gc*
# 只打印 GC 基本信息
-Xlog:gc
# 打印 GC 详细信息 + 原因
-Xlog:gc*,gc+phases=debug
# 打印堆内存信息
-Xlog:gc+heapGC 日志结构解析
Young GC 日志示例(Parallel GC)
2026-03-16T10:30:15.123+0800: 1.234: [GC (Allocation Failure)
[PSYoungGen: 262144K->32768K(305664K)]
262144K->45056K(1005568K), 0.0256789 secs]
[Times: user=0.05 sys=0.01, real=0.03 secs]逐行解析:
| 内容 | 说明 |
|---|---|
2026-03-16T10:30:15.123+0800 | GC 发生的日期时间 |
1.234 | JVM 启动后的秒数 |
[GC | GC 类型(Young GC) |
(Allocation Failure) | GC 原因:内存分配失败 |
[PSYoungGen: 262144K->32768K(305664K)] | 年轻代:GC 前->GC 后(总大小) |
262144K->45056K(1005568K) | 整个堆:GC 前->GC 后(总大小) |
0.0256789 secs | GC 耗时 |
user=0.05 sys=0.01, real=0.03 | CPU 时间(用户态/内核态/实际时间) |
Full GC 日志示例(Parallel GC)
2026-03-16T10:35:20.456+0800: 6.456: [Full GC (Ergonomics)
[PSYoungGen: 32768K->0K(305664K)]
[ParOldGen: 699392K->695808K(699392K)]
732160K->695808K(1005056K),
[Metaspace: 65536K->65536K(110592K)],
0.5678901 secs]
[Times: user=1.20 sys=0.05, real=0.57 secs]新增内容:
| 内容 | 说明 |
|---|---|
[Full GC | Full GC |
[ParOldGen: 699392K->695808K(699392K)] | 老年代变化 |
[Metaspace: ...] | 元空间变化 |
G1 GC 日志示例
2026-03-16T10:40:30.789+0800: 12.345: [GC pause (G1 Evacuation Pause) (young), 0.0152345 secs]
[Parallel Time: 12.5 ms, GC Workers: 4]
[Eden: 256.0M(256.0M)->0.0B(128.0M) Survivors: 16.0M->32.0M Heap: 512.0M(1024.0M)->288.0M(1024.0M)]
[CodeRoots Scanning: 0.1 ms]
[Clear CT: 0.2 ms]
[Other: 2.3 ms]
[Evacuation Failure: 0.0 ms]G1 特有信息:
- GC pause type:暂停类型(young / mixed / full)
- Eden/Survivors/Heap:各区域变化
- Parallel Time:并行处理时间
- GC Workers:并行线程数
常见 GC 原因
| 原因 | 说明 | 处理建议 |
|---|---|---|
Allocation Failure | 年轻代空间不足 | 正常,频繁则增大年轻代 |
Ergonomics | JVM 自动调节触发 | 检查是否需要调整参数 |
System.gc() | 代码调用 System.gc() | 禁用:-XX:+DisableExplicitGC |
CMS Generation Full | CMS 老年代满 | 增大老年代或调整 CMS 触发阈值 |
G1 Evacuation Pause | G1 对象复制暂停 | 正常,监控停顿时间 |
Metadata GC Threshold | 元空间达到阈值 | 增大元空间 |
Heap Dump Initiated GC | 堆转储触发 | jmap 触发,排查时出现 |
GC 日志分析工具
1. GCViewer(离线分析)
功能:
- 可视化 GC 日志
- 统计 GC 次数、耗时
- 内存趋势图
使用:
- 下载 GCViewer JAR
- 导入 gc.log 文件
- 查看分析结果
2. GCEasy(在线分析)
网址:https://gceasy.io/ (opens in a new tab)
功能:
- 上传日志自动分析
- 生成报告(GC 次数、耗时、内存泄漏检测)
- 提供优化建议
3. JClarity Censum
功能:
- 专业 GC 日志分析
- 内存泄漏检测
- 性能瓶颈定位
4. JDK 自带工具
# jstat 实时监控
jstat -gcutil <pid> 1000 # 每秒打印一次
# 输出示例
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 100.00 45.23 67.89 95.12 89.34 15 0.234 2 0.567 0.801
# 各列含义
# S0/S1: Survivor 区使用率
# E: Eden 区使用率
# O: 老年代使用率
# M: 元空间使用率
# YGC/YGCT: Young GC 次数/总耗时
# FGC/FGCT: Full GC 次数/总耗时典型问题诊断
问题一:Young GC 频繁
日志特征:
[GC (Allocation Failure) ...] # 每秒多次
[GC (Allocation Failure) ...]
[GC (Allocation Failure) ...]分析:年轻代太小,对象快速填满
解决:
-Xmn512m # 增大年轻代问题二:Full GC 频繁
日志特征:
[Full GC (Ergonomics) ...] # 短时间内多次
[Full GC (Ergonomics) ...]分析:老年代填充过快,可能是:
- 年轻代太小,对象过早晋升
- 内存泄漏
- 大对象直接进入老年代
解决:
- 增大年轻代:
-Xmn - 增大堆:
-Xmx - 排查内存泄漏:
jmap -histo <pid>
问题三:GC 耗时长
日志特征:
[Full GC ... 2.5678901 secs] # 单次 GC 耗时过长分析:
- 堆太大,GC 扫描时间长
- 大对象多
- 老年代碎片化(CMS)
解决:
- 调整堆大小
- 切换 G1:
-XX:+UseG1GC - 设置停顿目标:
-XX:MaxGCPauseMillis=200
问题四:内存泄漏
日志特征:
# 老年代使用率持续上升,Full GC 后仍不下降
[ParOldGen: 699392K->699390K(699392K)]分析:Full GC 后老年代回收效果差,存在内存泄漏
排查:
# 1. 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>
# 2. MAT 分析,查找大对象
# 3. 分析对象引用链,定位泄漏源GC 日志关键指标
健康指标
| 指标 | 健康值 | 异常值 |
|---|---|---|
| Young GC 频率 | 几秒一次 | 每秒多次 |
| Full GC 频率 | 几小时一次 | 每分钟一次 |
| Young GC 耗时 | < 50ms | > 100ms |
| Full GC 耗时 | < 500ms | > 1s |
| 老年代使用率 | < 70% | > 90% |
计算公式
Young GC 平均耗时 = YGCT / YGC
Full GC 平均耗时 = FGCT / FGC
GC 时间占比 = (YGCT + FGCT) / 运行总时间建议:GC 时间占比控制在 5% 以内
面试要点总结
| 问题 | 答案要点 |
|---|---|
| 如何开启 GC 日志? | JDK8 用 PrintGCDetails,JDK9+ 用 Xlog |
| Young GC 日志怎么看? | 关注年轻代变化、耗时、原因 |
| Full GC 频繁怎么办? | 排查内存泄漏、增大堆/年轻代、调整 GC 参数 |
| GC 日志分析工具? | GCEasy、GCViewer、jstat |
| 健康 GC 的指标? | Young GC 几秒一次 <50ms,Full GC 几小时一次 <500ms |
相关题目
- 如何通过 GC 日志判断内存泄漏?
- Young GC 和 Full GC 的日志有什么区别?
- System.gc() 触发的 GC 日志是什么样的?
- G1 的 GC 日志和 CMS 有什么不同?