GC日志需结合上下文带着问题阅读,核心是判断GC行为是否健康;必须开启完整参数(JDK8用-XX:+PrintGCDetails等,JDK9+用-Xlog:gc*),重点盯GC类型、停顿时间、内存变化率和老年代趋势,并用jstat、GCEasy或GCViewer交叉验证。

GC 日志不是“看一眼就知道问题在哪”的日志,它是一份需要结合上下文、带着问题去读的运行证据。核心目标就一个:判断当前 GC 行为是否健康——是不是在该回收的时候有效回收了,有没有异常堆积、频繁停顿或无效晋升。
先确认日志是否开全、格式是否可用
没开对参数,后面全白看。生产环境必须用带细节的配置:
- JDK 8:至少加 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M;别只用
-verbose:gc,它连晋升量、元空间变化都不报 - JDK 9+:统一用
-Xlog:gc*:file=gc.log:time,tags,level,filecount=5,filesize=10M,结构化强,支持按gc+heap或gc+metaspace单独过滤 - 检查日志里有没有出现
Allocation Failure、Metadata GC Threshold、Humongous Allocation这类触发原因字段——没有,说明日志可能被截断或参数没生效
重点盯住四类关键字段
不用逐行读,扫这几处就能快速定位倾向性问题:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
-
GC 类型与原因:比如连续出现
Allocation Failure且 Young GC 频繁,说明 Eden 太小或对象创建太快;出现Metadata GC Threshold就要查类加载是否失控;G1 出现大量Evacuation Failure,大概率是堆碎片或并发标记跟不上分配速度 -
停顿时间:关注
pause或real=xxx时间。Web 服务单次 Young GC 超过 50ms、Full GC 超过 200ms 就需干预;若连续多次 >100ms,基本可锁定 GC 是性能瓶颈 -
内存变化率:例如
[PSYoungGen: 1234M->210M(1536M)],算出回收率 ≈ (1234−210)/1234 ≈ 83%;长期低于 70%,说明对象存活率高(可能是缓存未清理、ThreadLocal 泄漏)或年轻代偏小 -
老年代趋势:看
Old或Tenured区使用量是否持续上升、Full GC 后下降不明显、或几分钟就来一次 Full GC——这九成是内存泄漏或对象过早晋升
配合命令和工具交叉验证
光看日志容易误判,必须和实时状态对得上:
立即学习“Java免费学习笔记(深入)”;
- 用
jstat -gcutil <pid> 1s 5查当前各区内存占用和 GC 次数/耗时,对比日志里的时间点是否吻合(比如日志里某次 Full GC 发生在 14:22:05,那此时jstat输出的FGC和FGCT应刚跳变) - 用
grep "Full GC" gc.log | wc -l统计频次;grep "pause" gc.log | awk '{sum+=$NF} END{print sum/NR}'算平均停顿时长 - 上传日志到 GCEasy(在线免费),它能自动标出 GC 耗时峰值、内存增长拐点、推荐调参项;本地偏好 GUI 可用 GCViewer,画出堆内存曲线图更直观
结合业务特征做最终判断
没有脱离场景的“标准值”。同一个 GC 行为,在不同系统里意义完全不同:
- 批处理任务允许单次 GC 停顿 2 秒,但要求低频;而支付接口要求 99% 的 Young GC 在 20ms 内完成
- 如果日志里
YGC次数多但每次回收干净、老年代平稳,那可能是业务本身对象生命周期短,未必是问题 - 发现
Concurrent Mode Failure(CMS)或to-space exhausted(G1),说明并发收集器已扛不住当前分配压力,优先考虑扩容堆或优化对象创建逻辑,而不是换收集器

















