标记-整理算法能解决老年代内存碎片问题,但因需移动大量存活对象、修正引用并全程stw,导致停顿时间长、cpu负载高,故现代gc仅将其作为备用或局部策略而非默认首选。

标记-整理算法确实在老年代中能有效解决内存碎片问题,但它的代价很实在——不是不能用,而是“用得越频繁,停顿越疼”。核心矛盾在于:整理 = 移动 + 修正引用 + 全线程暂停。
移动所有存活对象带来双重开销
老年代的特点是对象存活率高(常超过90%)。标记-整理在整理阶段必须把几乎全部存活对象逐个复制到内存一端。这不只是数据搬运:
- 每个对象的内容要从原地址拷贝到新地址;
- 所有指向该对象的引用(栈帧里的局部变量、其他对象的字段、常量池中的符号引用等)都必须同步更新为新地址;
- JVM需维护“转发指针”或依赖写屏障辅助修正,额外增加运行时负担。
Stop-The-World时间与存活对象数量强相关
整理阶段无法与用户线程并发执行,必须全程STW。而停顿时间不取决于垃圾量,而是取决于存活对象的总大小和数量:
- 假设老年代有4GB,其中3.8GB是存活对象,那么整理就要搬运3.8GB数据并修正数百万甚至上千万处引用;
- 一次Full GC若采用纯标记-整理,STW可能达数百毫秒甚至秒级,对延迟敏感服务不可接受;
- 相比之下,标记-清除虽产生碎片,但清除阶段只需遍历空闲列表,耗时短得多。
为什么现代GC仍保留整理思想,却不直接用它?
真正落地的老年代收集器(如CMS后期退化、G1的Mixed GC、ZGC/Shenandoah的并发整理)都做了关键取舍:
- CMS放弃整理,用标记-清除+碎片压缩(仅在必要时触发Serial Old回退);
- G1将老年代划分为小块(Region),只整理部分回收价值高的区域,避免全堆搬运;
- ZGC通过着色指针+读屏障实现对象移动与用户线程并发,把整理“隐身”掉,但底层仍是标记-整理逻辑的极致优化。
一句话总结
标记-整理解决了碎片,却把成本转嫁给了停顿时间和CPU负载;老年代因存活对象多,让它成为主力算法得不偿失。所以实际工程中,它更多是“备用方案”或“局部策略”,而非默认首选。










