线上 Full GC 频繁,如何排查解决?

#Java#面试八股

线上 Full GC 频繁,如何排查解决?

结论先行:Full GC 频繁先分两类:回收不动(老年代里全是活对象,泄漏或缓存过大)和回收太勤(对象晋升过快、元空间反复扩容、被显式触发)。判断只需一条:Full GC 之后老年代使用率是否明显下降。不降 → 查持有者;降了 → 查分配速率与晋升策略。然后再决定是改代码还是调参数。

一、症状与根因对照

症状可能的根因验证手段
Full GC 后老年代仍在 90% 以上内存泄漏、超大本地缓存heap dump + Dominator Tree
Full GC 后回落到 30% 但很快又满分配速率过高、缓存无上限jstat -gcutil 看 Eden 增速
元空间持续增长、Full GC 伴随 Metaspace 扩容动态类过多、热部署、CGLIB 膨胀jcmd GC.class_stats、-Xlog:class+load
日志出现 System.gc() 调用业务代码、RMI、DirectByteBuffer 清理-XX:+ExplicitGCInvokesConcurrent 或 Arthas stack
日志出现 concurrent mode failureCMS 并发收集追不上提前阈值、换 G1
日志出现 to-space exhausted / evacuation failureG1 没有空闲 Region 可搬调 G1ReservePercent、查 Humongous

二、排查路径

① 确认事实:多久一次、每次多久、回收效果
   jstat -gcutil <pid> 1000 60   → 看 O、FGC、FGCT、YGC
   grep "Pause Full" gc.log      → 看频率与停顿
        │
② 判断性质:Full GC 后老年代是否回落?
   ├─ 不回落 → 泄漏/大缓存 → dump → MAT 找 Path to GC Roots
   └─ 回落   → 分配太快/晋升太早 → 看 YGC 频率、Survivor 大小
        │
③ 定位触发源
   ├─ 元空间 → 动态类
   ├─ 显式 gc → 代码或框架
   └─ 晋升失败 → 担保、碎片、大对象
        │
④ 处置
   短期:扩容/限流/重启(保业务)
   长期:修代码/换收集器/调参(治根)

三、现场命令

# 1. 频率与回收效果(每 1 秒采一次,连采 60 次)
jstat -gcutil 1234 1000 60
# 列含义:S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
# 关注:O 的走势、FGC 是否在增长、FGCT 单次均值

# 2. 抓到 Full GC 前后的对象分布
jmap -histo:live 1234 | head -25

# 3. 导出堆,离线分析
jcmd 1234 GC.heap_dump /tmp/fullgc.hprof

# 4. 查显式 GC 的调用栈(Arthas)
# watch java.lang.System gc '{params, returnObj, throwExp}' -x 2 -n 5

四、常见处置与代价

处置立刻见效代价
扩大堆是单次停顿更长,泄漏仍在,只是推迟
降低缓存容量 / 加淘汰是命中率下降,回源压力上升
修泄漏(断开引用)是需要定位与发版
CMS 换 G1是需要压测验证停顿目标
提前 CMS 阈值部分更频繁的并发收集,CPU 占用上升
关闭 System.gc()视情况可能影响堆外内存清理时机

五、一个典型的误判

「堆从 8G 扩到 16G 后 Full GC 少了」,看起来解决了,实际上泄漏只是被更大的空间掩盖:对象增长曲线不变,爆发时间点只是推迟了一倍。判断是否真正解决,要看单位业务量的老年代增长速率是否下降,而不是 Full GC 次数。

# 计算「每秒晋升量」:连续两次采样老年代已用字节数之差 / 时间
jstat -gc 1234 5000 10 | awk 'NR>1 {if (prev) printf "晋升速率 %.1f MB/s\n", ($6+$8-prev)/5/1024; prev=$6+$8}'

六、常见追问

追问回答要点
Full GC 频繁但每次都很短,要紧吗要紧,说明老年代在反复扫,CPU 被持续消耗,尾延迟会抖
能否在线不重启解决多数不能,但可以限流、临时扩堆、关闭缓存写入;根治仍需发版
怎么证明是代码泄漏dump 中同一批对象持续增长、且 Path to GC Roots 指向业务静态容器
G1 下 Full GC 有什么特别常见原因是 Humongous 分配失败或转移失败,重点查大数组与 G1ReservePercent
监控该配什么指标Full GC 次数与停顿、老年代使用率、晋升速率、元空间使用率,四个一起看

小结

先分清「回收不动」还是「回收太勤」,再顺着触发源定位到元空间、显式调用、晋升失败或泄漏。扩容只能买时间,判断是否真解决,要看单位业务量的内存增长速率。

笔记加载中…