新生代采用复制算法,老年代采用标记-清除或标记-整理算法。因新生代对象存活率低(约2%),复制算法仅处理少量存活对象,速度快且无碎片;老年代对象存活率高,复制开销大,故用标记类算法平衡空间与时间成本。

因为新生代对象“朝生夕灭”,存活率极低(约98%),而老年代对象长期存活、存活率高——这种本质差异决定了必须用不同算法,否则效率会严重下降。
新生代用复制算法:专治短命对象
新生代里绝大多数对象只活一个GC周期,Minor GC时只需处理少量存活对象。复制算法把Eden区和一个Survivor区里的存活对象,一次性复制到另一个空的Survivor区,再清空原区域。
- 只复制存活对象,不遍历全堆,速度极快(毫秒级停顿)
- 复制后内存天然连续,彻底避免碎片问题
- Eden:Survivor默认8:1:1,空间浪费仅10%,性价比高
- 对象年龄达阈值(默认15)或Survivor放不下时,直接晋升老年代
老年代用标记-清除或标记-整理:应对高存活率
老年代对象经过多次Minor GC仍存活,数量多、生命周期长。若用复制算法,每次都要搬运大量对象,开销巨大且得预留同等大小的空闲空间,根本不现实。
- 标记-清除:先标记存活对象,再统一清除垃圾;速度快但留碎片,影响大对象分配
- 标记-整理:标记后把存活对象往一端压缩,再清理边界外内存;无碎片但移动成本高
- G1、ZGC等现代收集器在老年代区域化处理,但底层仍基于标记+局部整理逻辑
分代不是硬性隔离,而是动态协同
新生代和老年代之间存在真实引用(比如老年代缓存持有了新生代刚创建的DTO),但这类跨代引用极少——这就是“强代假说”的实践基础。JVM通过卡表(Card Table)等机制高效追踪跨代引用,避免每次GC都扫描全堆。
- Minor GC只扫描新生代 + 卡表标记的少量老年代页
- Full GC才真正遍历所有代,但应尽量避免
- 晋升机制(年龄阈值、动态空间担保)让对象在合适时机迁移,维持各代负载平衡
算法选择本质是成本权衡
没有“最好”的算法,只有“最合适”的场景。新生代复制算法牺牲空间换时间,老年代标记类算法牺牲时间换空间利用率——分代设计把这两种权衡拆开,在各自区域内做到极致。
- 新生代:时间敏感,停顿必须短,允许10%空间冗余
- 老年代:空间敏感,碎片可能引发OOM,宁可多花点时间整理
- 不分代的话,要么全堆复制(不可能),要么全堆标记(太慢),二者都不可行
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











