JVM 堆内存分代模型
JVM 把堆内存分成不同「世代」,对不同生命周期的对象使用不同的垃圾回收策略,提升 GC 效率。
分代结构
| 分代 | 存储对象 | 回收策略 | 特点 |
|---|---|---|---|
| 新生代(Young) | 刚创建的「短命对象」 | 频繁回收(YGC/Minor GC) | 回收快、停顿短(毫秒级) |
| 老年代(Old) | 存活久的「长命对象」 | 少回收(Mixed GC/FGC) | 回收慢、停顿长(秒级) |
元空间(Metaspace,JDK8+)是非堆内存,存储类元数据。
新生代(Young)
新生代是新对象分配的区域,细分为 3 个区,包括Eden/S0和S1,具体如下:
Eden 区(伊甸园)
- 所有新对象优先分配到这里
- 占新生代约 80%
- YGC 的主要回收区域
Survivor 区(幸存区,S0 / S1)
- 两个等大区域,同一时刻只有一个在使用
- 作用:Eden 满触发 YGC 时,Eden 中存活对象移到 Survivor
- 对象每经历一次 YGC 存活,「年龄+1」,达到阈值(默认 15)后晋升到老年代
- Survivor 0(S0 / From Survivor)
- Survivor 1(S1 / To Survivor)
S0 和 S1 交替工作:一个存存活对象(From),另一个空着等垃圾回收时接收对象(To),同一时刻只有一个在使用。
YGC 完整流程
- Eden 满 → 触发 YGC
- 回收 Eden 死对象,存活对象移到空的 Survivor 区
- Survivor 满 / 对象年龄到阈值 → 移到老年代
- 清空 Eden,两个 Survivor 角色互换
老年代(Old)
- 来源:Survivor 晋升来的对象 + 大对象(直接分配)
- G1GC 下主要靠并发标记 + 混合回收清理
- G1GC(Garbage-First GC)的设计目标是尽量减少 STW(Stop-The-World)时间。它把堆划分为多个大小相等的 Region (区域),老年代不再是一个连续区域。
- 并发标记(Concurrent Marking) — 在应用运行时,G1 后台线程标记出老年代中仍然存活的对象。这个过程是并发执行的,不暂停应用。
- 混合回收(Mixed GC) — 标记完成后,G1 不会只回收新生代,而是一次性回收新生代 + 部分老年代 Region(优先回收垃圾最多的 Region,即 “Garbage-First” 的由来)。
- 老年代满 → 触发 Full GC(整堆回收,STW,性能杀手)
- 指Parallel GC / CMS 等非 G1GC 的场景,或者是 G1GC 也兜不住的情况。
- Full GC:
- 对整个堆(新生代 + 老年代 + 元空间)进行全量回收
- 全程 STW(Stop-The-World) — 所有应用线程暂停
- 时间很长,通常几百毫秒到几秒甚至更久
- 性能杀手:在延迟敏感的应用中,几秒钟的停顿可能导致连接超时、请求失败、服务雪崩。尤其是 Full GC 频率高时(比如一小时一次),系统会周期性”卡死”
典型的Full GC 触发条件
| GC 策略 | 触发 Full GC 的常见原因 |
|---|---|
| Parallel GC | 老年代空间碎片化/不足 |
| CMS | 并发模式失败(Concurrent Mode Failure) |
| G1GC | 并发标记赶不上对象分配速度 → 晋升失败(Promotion Failure) |
分代设计目的
- 新生代(占堆 1/3):频繁扫描,空间小、回收快(YGC 单次 20-50ms)
- 老年代(占堆 2/3):很少扫描,快满时才回收,避免频繁全堆扫描
相关笔记
- jstat-命令详解
- JVM-OOM排查指南
- JVM内存溢出预防
- Java-启动参数
- JVM-对象创建与内存分配 — 对象创建流程与 TLAB
- JVM-GC类型对比 — 常见 GC 类型与回收器对比
- 快手电商-一面-19题总结 — Q2 对象从 new 到 GC