标记复制算法在新生代采用“隐式标记+顺序复制”一体化机制,适合“朝生夕死”特性;hotspot用eden:survivor:survivor=8:1:1三区结构,通过调整新生代大小、survivorratio、maxtenuringthreshold等参数优化效率。

标记复制算法在新生代中不是“先标记再复制”的两阶段流程,而是以“隐式标记+顺序复制”为核心的一体化回收机制——它不显式维护标记位,而是直接从 GC Roots 出发扫描存活对象,边扫描边复制到目标空间,一次完成对象搬迁与内存清理。
为什么新生代适合用标记复制算法
新生代对象具有典型的“朝生夕死”特征:约98%的对象在首次 Minor GC 时已不可达。这使得复制算法天然适配:
- 只需处理极少量存活对象(通常仅2%左右),复制开销极小
- Eden + 一个 Survivor 区即为本次扫描范围,无需遍历整个新生代
- 复制后目标 Survivor 区内对象天然紧凑、无碎片,分配只需移动指针
- 清空源区是批量归零操作,无逐个释放成本
HotSpot 中的实际实现:Appel 式三区结构
并非传统意义上的均分两块空间,而是采用 Eden:Survivor:Survivor = 8:1:1 的三区划分:
- 新对象全部分配在 Eden 区;两个 Survivor 区(From/To)交替使用
- Minor GC 时,扫描 Eden 和当前 From 区的所有存活对象
- 存活对象按年龄判断去向:未达晋升阈值的复制到 To 区,已达阈值或 To 区放不下则直接晋升老年代
- GC 结束后,Eden 和 From 区整体清空,To 区成为新的 From 区,角色互换
影响复制效率的关键配置点
算法本身高效,但效果依赖参数与运行环境协同:
- 新生代大小:建议设为堆总内存的 1/4~1/3;过大则扫描/复制耗时上升,过小则 GC 频繁、总停顿增加
- SurvivorRatio 设置:默认 8 表示 Eden:Survivor=8:1;若对象平均存活 2–3 次才晋升,可调小该值(如 -XX:SurvivorRatio=6),增大 Survivor 容量,减少担保失败
- 晋升阈值 MaxTenuringThreshold:默认 15,可根据实际对象生命周期下调(如设为 6),避免短命对象滞留 Survivor 区反复复制
- 禁用 System.gc():该调用会触发 Full GC,STW 时间远超 Minor GC,必须在代码和监控中杜绝
如何验证和优化复制过程
不能只看“是否用了复制算法”,而要看它是否真正快且稳:
- 开启详细 GC 日志:-Xlog:gc*,gc+phases=debug -XX:+PrintGCDetails,重点关注 “Root scanning” 和 “Object copy” 阶段耗时占比
- 检查安全点停顿:避免超长计算逻辑阻塞线程进入 Safepoint,否则 GC 启动延迟拉长整体 STW
- 观察晋升行为:若大量对象在年轻代未满龄就因 Survivor 溢出而提前晋升,说明 Survivor 空间不足或对象存活率偏高,需调整比例或检查业务逻辑
- 慎用并行收集器:Serial 是单线程复制,ParNew/G1/ZGC 支持多线程复制,但需权衡 CPU 资源与吞吐平衡











