复制算法适合新生代,因其仅处理2%~5%存活对象,通过指针翻转实现零搬运、无碎片、低停顿;eden占80%分配,survivor双区轮转、动态角色切换,年龄按跨区复制次数增长,空间比8:1:1兼顾效率与成本。

复制算法在新生代中高效,关键不在“搬数据”,而在“换指针”——它用极简的逻辑规避碎片、压缩停顿、适配对象短命特性。
为什么新生代特别适合复制算法
新生代对象98%生命周期极短(IBM实测),每次Minor GC后仅约2%~5%存活。复制算法只处理这少量存活对象,省去扫描全堆、整理碎片、维护空闲链表等开销。相比之下,标记-清除在新生代会快速积累碎片,导致分配失败频发;标记-整理则因频繁移动大量对象而STW显著拉长。
- Eden区承担约80%新对象分配,天然形成“大块集中写入”模式,配合指针碰撞(Bump the Pointer)实现O(1)分配
- Survivor区双区轮转,确保每次GC都有确定、干净、连续的接收目标,不依赖内存碎片整理
- 存活对象按复制顺序紧密排列,后续分配可直接推进指针,无须查找可用空闲块
S0与S1不是静态分区,而是动态角色
S0和S1名称固定,但From/To角色每轮Minor GC后翻转一次。这个翻转不挪动任何字节,只是JVM内部更新一个指向空闲Survivor区的指针。例如:
- 本轮GC:Eden + S0中存活对象 → 全部复制到S1,完成后清空Eden和S0 → 此时S1为“已用”,S0变为空闲
- 下一轮GC:Eden + S1中存活对象 → 全部复制到S0,完成后清空Eden和S1 → S0成为新“已用”,S1再次空闲
- GC日志中可见S0 used从0%跳至42%,S1 used从42%归零,这就是角色切换的直接证据
对象年龄与晋升不是靠时间,而是靠复制次数
对象年龄(age)只在**进入Survivor区**时递增,且仅限跨Survivor复制(Eden→S0算age=1,S0→S1算age=2,S1→S0算age=3)。它不随时间增长,也不因Minor GC次数自动加1。
- 默认MaxTenuringThreshold=15,但实际晋升常更早:若某次GC后,所有age≥n的对象总大小超过另一个Survivor区容量的一半,则所有age≥n的对象全部晋升(动态年龄判定)
- Survivor空间不足时,直接触发HandlePromotionFailure机制,溢出对象担保进入老年代,不等待年龄达标
- 设-XX:MaxTenuringThreshold=0,等效于禁用Survivor缓冲,所有对象在第一次Minor GC后即晋升老年代
空间比例设计兼顾效率与成本
默认Eden:S0:S1 = 8:1:1(由-XX:SurvivorRatio=8控制),使90%堆空间可用于对象分配,仅10%作复制缓冲。这种非对称划分突破了经典复制算法“必须50%闲置”的限制,又保留了无碎片优势。
- 不能单独调大S0或禁用S1:JVM启动即固化结构,破坏对称性会导致GC逻辑异常或对象误晋升
- 过小的Survivor区会加剧提前晋升,增加老年代压力;过大会浪费空间,降低Eden吞吐
- 真实场景中,可通过观察GC日志中Survivor occupancy变化趋势,结合promotion rate调整SurvivorRatio











