Full GC是JVM在老年代空间不足时自动触发的全局垃圾回收,涵盖年轻代、老年代及元空间;典型触发场景包括晋升失败、大对象直接分配、老年代高水位、System.gc()调用等。

当老年代空间不足时,JVM 会触发 Full GC 来尝试回收整个堆(包括年轻代、老年代)以及方法区(元空间)的垃圾,从而释放内存。但这不是“主动调用”的操作,而是 JVM 基于内存分配和回收策略自动触发的行为。关键在于理解触发条件、影响因素和可优化方向。
Full GC 触发的典型场景
以下情况较容易导致老年代空间不足并触发 Full GC:
- 年轻代对象晋升失败:一次 Minor GC 后,大量存活对象需晋升到老年代,但老年代剩余空间不足以容纳,直接触发 Full GC
- 大对象直接分配在老年代(如 -XX:PretenureSizeThreshold 设置后),且老年代碎片化或空间紧张
- 老年代本身已长期高水位运行(比如长期缓存未清理、内存泄漏),再有新对象晋升就立即触顶
- System.gc() 被显式调用(不推荐),且未禁用(-XX:+DisableExplicitGC 未启用)
如何让 Full GC 更有效地清理老年代
Full GC 的效果取决于所用的垃圾收集器及其参数配置。不同收集器行为差异较大:
- 使用 CMS 收集器时,若发生 Concurrent Mode Failure(并发清理来不及),会退化为 Serial Old 进行 Full GC,停顿时间长且效率低;应调优 -XX:CMSInitiatingOccupancyFraction 或改用 G1
- G1 收集器中,“Full GC”已是退化行为,通常说明 Mixed GC 无法及时回收,需检查 -XX:G1HeapWastePercent、-XX:G1MixedGCCountTarget 等参数
- ZGC / Shenandoah 在 JDK 11+ 中基本消除传统 Full GC 概念,老年代压力通过并发标记与转移缓解
- 若仍在用 Parallel Old(默认搭配 Parallel Scavenge),Full GC 是单线程 Stop-The-World,务必避免频繁发生
排查与预防老年代不足的核心动作
不能只依赖 Full GC 清理,而要定位根本原因:
- 开启 GC 日志(-Xlog:gc*:file=gc.log:time,uptime,level,tags)观察每次 Full GC 前后的老年代使用量、回收量、是否成功
- 用 jstat -gc
定期查看 S0C/S1C/EC/OC/MC/CCSC 等指标,重点关注 OC(Old Capacity)和 OU(Old Used)持续增长趋势 - 借助 jmap -histo:live
或 MAT 分析堆快照,确认是否存在生命周期过长的对象(如静态集合、未关闭的连接池、监听器未注销) - 检查是否有不合理的对象晋升策略,例如 Survivor 区过小(-XX:SurvivorRatio 太大)、MaxTenuringThreshold 设置过高,导致对象过早进入老年代
合理设置堆结构与回收目标
避免老年代“突然不够用”,需结合应用特征预估和约束:
- 根据对象平均生命周期,调整 -Xmn(年轻代大小),避免年轻代过小导致频繁 Minor GC 和过早晋升
- 老年代不宜过小,一般建议占堆总大小的 60%~75%,但也要留足空间应对突发流量或缓存膨胀
- 启用 -XX:+UseG1GC 并设置 -XX:MaxGCPauseMillis=200(示例值),让 G1 主动控制老年代回收节奏,减少 Full GC 概率
- 对长时间运行的服务,考虑启用 -XX:+AlwaysPreTouch 预先分配并锁定内存页,减少 GC 过程中的系统调用开销

















