JVM垃圾回收由内存状态动态触发:Minor GC在Eden区无连续空间或Survivor区不足时发生;Major GC实为老年代紧张引发的回收,常伴Full GC;Full GC由老年代/元空间耗尽、System.gc()调用或堆外内存压力导致。

JVM 垃圾回收不是按固定时间运行的,而是由内存使用状态和运行时条件动态决定的。判断触发时机,关键看“堆内存是否告急”以及“JVM是否收到明确信号”。下面从三类常见 GC 出发,说清楚每种情况怎么识别、为什么发生。
Minor GC(新生代回收)什么时候发生
这是最频繁的一类 GC,主要盯 Eden 区。
- 当新对象要分配内存,但 Eden 区已没有连续空间容纳它时,立刻触发 Minor GC
- 即使 Eden 没完全填满,如果 Survivor 区空间不足,无法容纳从 Eden 复制过来的存活对象,也可能提前触发
- 可通过 -XX:+PrintGCDetails 查看日志中类似
GC pause (G1 Evacuation Pause) (young)或ParNew的标记,确认是新生代回收
Major GC(老年代回收)实际怎么被感知
严格来说,“Major GC”在 HotSpot 中并不是一个独立事件,它通常表现为老年代空间紧张后的一次回收动作,常与 Full GC 混用或伴随发生。
- 当 Minor GC 后,有大量对象需要晋升到老年代,但老年代剩余空间 小于晋升所需空间 + 一定担保余量,就会触发老年代回收(或直接升级为 Full GC)
- 老年代使用率持续接近阈值(如 CMS 默认 92%,G1 通过启发式预测),也会主动启动并发回收阶段
- 日志中出现
Concurrent Mode Failure或promotion failed,基本说明老年代已扛不住了
Full GC 触发的典型信号有哪些
这是影响最大、停顿最长的回收,需重点排查。
- 老年代空间不足且 Major GC 后仍无法腾出足够空间:这是最常见的原因
-
元空间(Metaspace)耗尽:加载太多类(如热部署、反射生成大量代理类),日志会报
java.lang.OutOfMemoryError: Metaspace - 显式调用 System.gc():虽只是建议,但在默认配置下(未加 -XX:+DisableExplicitGC)往往真会触发一次 Full GC
- 堆外内存压力传导:比如 DirectByteBuffer 分配失败,可能间接触发 Full GC 来尝试释放老年代中的大对象
怎么快速验证当前 GC 状态
不靠猜,靠观察:
- 启动参数加上 -XX:+PrintGCDetails -XX:+PrintGCDateStamps,看控制台或日志里每次 GC 的类型、耗时、前后内存变化
- 用 jstat -gc
实时查看各代内存使用率、GC 次数和耗时(重点关注 YGC、FGC 列) - 发现频繁 Full GC 时,用 jmap -dump:format=b,live,file=heap.hprof
抓堆快照,再用 MAT 分析大对象和泄漏点


















