知识模块
☕ Java 知识模块
四、JVM 深入理解
GC 日志分析

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+heap

GC 日志结构解析

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+0800GC 发生的日期时间
1.234JVM 启动后的秒数
[GCGC 类型(Young GC)
(Allocation Failure)GC 原因:内存分配失败
[PSYoungGen: 262144K->32768K(305664K)]年轻代:GC 前->GC 后(总大小)
262144K->45056K(1005568K)整个堆:GC 前->GC 后(总大小)
0.0256789 secsGC 耗时
user=0.05 sys=0.01, real=0.03CPU 时间(用户态/内核态/实际时间)

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 GCFull 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年轻代空间不足正常,频繁则增大年轻代
ErgonomicsJVM 自动调节触发检查是否需要调整参数
System.gc()代码调用 System.gc()禁用:-XX:+DisableExplicitGC
CMS Generation FullCMS 老年代满增大老年代或调整 CMS 触发阈值
G1 Evacuation PauseG1 对象复制暂停正常,监控停顿时间
Metadata GC Threshold元空间达到阈值增大元空间
Heap Dump Initiated GC堆转储触发jmap 触发,排查时出现

GC 日志分析工具

1. GCViewer(离线分析)

功能

  • 可视化 GC 日志
  • 统计 GC 次数、耗时
  • 内存趋势图

使用

  1. 下载 GCViewer JAR
  2. 导入 gc.log 文件
  3. 查看分析结果

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) ...]

分析:老年代填充过快,可能是:

  1. 年轻代太小,对象过早晋升
  2. 内存泄漏
  3. 大对象直接进入老年代

解决

  1. 增大年轻代:-Xmn
  2. 增大堆:-Xmx
  3. 排查内存泄漏:jmap -histo <pid>

问题三:GC 耗时长

日志特征

[Full GC ... 2.5678901 secs]  # 单次 GC 耗时过长

分析

  1. 堆太大,GC 扫描时间长
  2. 大对象多
  3. 老年代碎片化(CMS)

解决

  1. 调整堆大小
  2. 切换 G1:-XX:+UseG1GC
  3. 设置停顿目标:-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

相关题目

  1. 如何通过 GC 日志判断内存泄漏?
  2. Young GC 和 Full GC 的日志有什么区别?
  3. System.gc() 触发的 GC 日志是什么样的?
  4. G1 的 GC 日志和 CMS 有什么不同?

参考资料