复制算法通过Eden区与两个Survivor区(S0/S1)协同,以“搬移存活对象+清空原区域”实现回收;每次Minor GC时,存活对象复制到空的To区并年龄+1,S0/S1角色轮换,空间不足或满足动态年龄条件时对象晋升老年代。

复制算法在新生代中实际运行时,核心就是 Eden 区和两个 Survivor 区(S0 和 S1)之间的协同配合。它不靠“清理”,而是靠“搬移存活对象 + 清空原区域”来完成回收,关键在于每次只用一块 Survivor 区作为目标,另一块保持空闲,从而保证复制过程有落脚点。
Eden 区填满触发 Minor GC
新对象默认分配在 Eden 区。当 Eden 区空间不足时,JVM 立即发起 Minor GC。此时,存活对象只存在于 Eden 区和当前的 “From Survivor” 区(比如 S0),而 “To Survivor” 区(比如 S1)是空的,专用于接收复制的对象。
- GC 开始前,Eden 和 From Survivor 中的对象都参与可达性分析(从 GC Roots 出发标记存活)
- 所有被标记为存活的对象,按规则决定去向:年龄未达阈值、且 To 区能容纳,就复制过去;否则直接进入老年代
- 复制完成后,Eden 和 From Survivor 被整体清空(无需逐个擦除,直接重置指针)
Survivor 区角色动态轮换
每次 Minor GC 后,From 和 To 的角色会互换。比如本次使用 S0 作 From、S1 作 To,下一次就变成 S1 作 From、S0 作 To。这种设计确保:
- 始终有一块 Survivor 区是干净的、可写入的
- 避免同一块 Survivor 区反复读写导致数据覆盖或状态混乱
- 天然支持对象年龄计数——每次成功复制到 To 区,年龄就 +1
对象晋升不只是看年龄
虽然默认年龄 ≥15 才晋升老年代,但实际还有两类提前晋升场景:
- 动态年龄判定:若某次 GC 时,Survivor 中相同年龄的所有对象总大小 ≥ Survivor 空间一半,则所有 ≥ 该年龄的对象全部晋升
- To 区空间不足:Eden 或 From 中的存活对象准备复制时,发现 To 区剩余空间不够,这些对象直接进入老年代(也叫“担保机制”)
- 大对象(如长数组)可能跳过 Survivor,直接分配到老年代(由 -XX:PretenureSizeThreshold 控制)
复制算法的代价与收益
它用空间换时间:需要预留一块空的 Survivor 区(占新生代 10%),但换来的是极快的回收速度和绝对干净的内存布局。
- 没有内存碎片——复制后 To 区对象紧凑排列,下次分配直接指针碰撞即可
- 只处理存活对象,不扫描死亡对象,效率远高于标记-清除
- 缺点是浪费一半 Survivor 空间(实际新生代可用率约 90%,因 Eden 占 8/10)

















