Full GC 频繁通常是什么原因?怎么定位与处理?

结论先行:Full GC(FGC)频繁的本质只有一句话:老年代“快速被填满”或“回收效率太低”。高频原因包括堆配得偏小、对象过早晋升、大对象直入老年代、内存泄漏、元空间不足,以及代码里显式调用 System.gc()。处理顺序一定是“先量化取证,再对症下药”,而不是盲目加内存或换垃圾收集器。

常见原因与现象对照表

原因典型现象处理方向
堆整体偏小FGC 频繁、Old 区长期高水位合理扩堆或降低对象存量
对象过早晋升Young 区偏小、Survivor 放不下调 -Xmn 与晋升阈值 MaxTenuringThreshold
大对象直入老年代频繁超大数组/超大集合代码层拆分对象、分批处理
内存泄漏Old 区只涨不降、曲线爬坡导出快照,MAT 追 GC Roots
元空间不足/频繁扩容类加载异常、Metaspace 水位抖动调大 MaxMetaspaceSize,查动态生成类
显式 System.gc()FGC 频繁但堆水位不高排查 RMI 定时 GC 等,必要时 DisableExplicitGC

定位步骤

  1. 先用 jstat -gcutil <pid> 5000 连续采样,记录 FGC 次数增速与 Old 区占比;
  2. jmap -histo:live <pid> 看存活对象分布,锁定数量异常的类;
  3. 画内存曲线:水位平稳但 FGC 多 → 参数或晋升问题;曲线持续爬坡 → 泄漏;
  4. 拿不准就导堆快照(隔一段时间导两份做对比),用 MAT 确认根因归属。

处理示例

# 采样 GC 数据(每 5 秒一次,共 10 次)
jstat -gcutil 12345 5000 10
# 查看存活对象 Top 类,定位大头对象
jmap -histo:live 12345 | head -30

常见追问 / 记忆点

  • 追问:怎么区分“对象就是多”和“对象泄漏”?答:间隔导出两份堆快照,同一类对象数量只增不减即为泄漏。
  • 追问:Metaspace 也会引发 Full GC 吗?答:会,元空间触发扩容或回收时可能伴随 Full GC,注意观察 GC 日志中的 class 相关信息。
  • 记忆点:FGC 频繁 ≠ 堆不够大;先看 Old 区水位曲线,“参数止血 + 快照归因”才是完整答案。
笔记加载中…