分代收集是基于对象存活周期动态调整回收策略的机制,新生代用复制算法处理短命对象(Eden+S0/S1按8:1:1划分),老年代用标记-清除/整理算法处理长存对象,并通过记忆集优化跨代引用扫描。

分代收集不是硬性切分内存,而是顺着对象真实的“活多久”来动态安排回收策略。它不靠人拍脑袋定规则,而是基于大量实证——比如约 98% 的对象在新生代一次 GC 后就消亡,而活过几次的对象,后续存活概率会快速上升。
新生代:专为“快进快出”的对象设计
新生代处理高频创建、短命的对象,结构上分为 Eden 区和两个 Survivor 区(S0/S1),默认比例是 8:1:1。
- 新对象优先分配到 Eden;Eden 满时触发 Minor GC,采用复制算法——只保留少量存活对象,复制到空闲 Survivor 区,再清空 Eden 和原 Survivor
- 每次 Minor GC 后仍存活的对象,年龄计数器 +1;在 Survivor 区间来回复制,达到阈值(如默认 15 次)或空间不足,就晋升到老年代
- 大对象(如长数组)可能直接绕过新生代,进入老年代,避免反复复制带来的开销
老年代:留给“稳定长存”的对象
老年代存放的是经多次 Minor GC 淘汰后依然存活的对象,以及直接分配的大对象。这里对象存活率高、回收频率低。
- 不适合复制算法(空间浪费大、移动成本高),主流使用标记-清除或标记-整理算法
- Major GC 或 Full GC 才会触达老年代,停顿时间通常明显长于 Minor GC
- 老年代空间不足时,可能触发担保失败(Promotion Failure),进而引发 Full GC;JVM 会基于剩余空间、历史晋升速率等动态估算是否安全晋升
跨代引用:用记忆集(Remembered Set)解决扫描爆炸问题
如果老年代对象引用了新生代对象,Minor GC 时若要确保可达性,就得扫描整个老年代——这会彻底拖慢回收速度。分代引入记忆集破局。
立即学习“Java免费学习笔记(深入)”;
- 记忆集以卡表(Card Table)形式把老年代划分为固定大小(如 512 字节)的卡页
- 当老年代对象引用新生代对象时,对应卡页被标记为“脏”
- Minor GC 时只需扫描这些脏卡页内的老年代对象作为 GC Roots 补充,大幅缩小扫描范围
分代是动态演化的策略,不是静态地图
对象年龄、晋升阈值、Survivor 之间的流转、记忆集的维护与清理,都在运行时持续调整。它不是一成不变的内存分区图,而是随程序行为自适应演化的回收节奏。


















