什么是 Minor GC(Young GC)、Major GC(Old GC)和 Full GC?
在 Java 虚拟机(JVM)的内存管理中,垃圾回收(Garbage Collection, GC)是自动管理内存的核心机制。为了提高回收效率,JVM 通常采用分代收集算法,将堆内存分为新生代(Young Generation)和老年代(Old Generation)。
基于这种内存划分,GC 也被分为了三种主要类型:Minor GC (Young GC)、Major GC (Old GC) 和 Full GC。
以下是它们的详细解释和区别:
1. Minor GC(又称 Young GC)
Minor GC 是只针对新生代(Young Generation)进行的垃圾回收。
- 作用区域:新生代(包括 Eden 区和两个 Survivor 区:S0 和 S1)。
- 触发条件:当 JVM 无法为新创建的对象在 Eden 区分配内存空间时(即 Eden 区满了),就会触发 Minor GC。
- 执行过程:
- 扫描新生代,将 Eden 区和目前正在使用的 Survivor 区中存活的对象,复制到另一个空白的 Survivor 区中。
- 如果存活的对象年龄(经历过的 Minor GC 次数)达到了阈值(默认通常是 15),或者 Survivor 区空间不足,这些对象会被晋升(Promote)到老年代。
- 清空 Eden 区和原来的 Survivor 区。
- 特点:
- 非常频繁:因为 Java 中大多数对象都是“朝生夕灭”的(生命周期极短)。
- 速度极快:新生代空间相对较小,且存活对象少,复制成本低。
- 会引发 STW (Stop-The-World):所有应用线程都会暂停,但因为回收速度极快,停顿时间通常在毫秒级,用户几乎无感知。
2. Major GC(又称 Old GC)
Major GC 是只针对老年代(Old Generation)进行的垃圾回收。
- 作用区域:老年代。
- 触发条件:
- 老年代空间不足时。
- 在某些垃圾回收器(如 CMS,Concurrent Mark Sweep)中,当老年代内存使用率达到一定阈值(如 92%)时会被触发。
- 特点:
- 频率较低:老年代存放的都是长生命周期的对象。
- 速度较慢:老年代空间通常比新生代大,且存活对象多,通常采用“标记-清除”或“标记-整理”算法,比 Minor GC 慢 10 倍以上。
- 会引发 STW:停顿时间比 Minor GC 长得多。
- ⚠️ 概念易混淆点:在很多语境中,大家习惯把 Major GC 和 Full GC 混为一谈。但在严格的定义下,Major GC 仅指老年代的回收。不过在实际的垃圾回收器(如 Parallel Scavenge)实现中,老年代满了往往会连带触发一次 Young GC,因此界限有时会比较模糊。
3. Full GC
Full GC 是对整个 JVM 内存空间进行的彻底清理。
- 作用区域:整个堆内存(新生代 + 老年代)以及 方法区/元空间(Metaspace)。
- 触发条件(情况较多,通常是系统面临内存危机的表现):
- 老年代空间不足:新生代对象晋升到老年代时,老年代没有足够的连续空间。
- 空间分配担保失败(Promotion Failure):Minor GC 前,JVM 发现老年代的最大可用连续空间小于新生代所有对象总空间,且不允许冒险,或者冒险失败。
- 元空间(Metaspace)不足:加载的类信息过多导致方法区爆满。
- 显式调用
System.gc():代码中手动建议 JVM 执行 GC(JVM 可能会忽略,但通常会导致 Full GC)。 - CMS GC 异常(Concurrent Mode Failure):在使用 CMS 垃圾回收器时,垃圾回收还没执行完,用户的并发线程又产生了大量垃圾塞满老年代。
- 特点:
- 速度极慢:因为要扫描和清理整个 JVM 内存。
- 严重的 STW:会导致应用程序出现明显的停顿(卡顿),停顿时间可能长达数秒甚至更久。
- 性能杀手:JVM 调优的核心目标之一就是尽量减少甚至消除 Full GC。
总结与对比表格
| 特性 | Minor GC (Young GC) | Major GC (Old GC) | Full GC |
|---|---|---|---|
| 回收区域 | 仅新生代 (Eden + Survivor) | 仅老年代 | 整个堆 (新生代+老年代) + 元空间 |
| 触发条件 | Eden 区空间不足 | 老年代空间不足/达到阈值 | 老年代满、元空间满、分配担保失败、System.gc() |
| 发生频率 | 非常高 | 较低 | 极低(应尽量避免) |
| 执行速度 | 极快(毫秒级) | 较慢(比 Minor 慢 10倍+) | 极慢(秒级甚至更长) |
| STW影响 | 有,但通常微乎其微 | 有,停顿较明显 | 有,导致严重的系统卡顿 |
| 调优目标 | 尽量让短命对象在 Minor GC 被回收 | 减少大对象直接进入老年代 | 极力避免 |
💡 给开发者的建议:
- 避免创建过大的对象(如极长的数组),因为大对象会直接进入老年代,加速触发 Full GC。
- 警惕内存泄漏(如长时间把对象放在静态的
HashMap中不清理),这会导致老年代被撑满,频繁引发 Full GC 甚至 OOM。 - 如果你的系统经常发生长时间卡顿,第一步应该去查看 GC 日志,确认是否正在频繁发生 Full GC。
右滑查看面试常问