标记-整理算法核心目标是解决老年代内存碎片问题,先标记存活对象,再将其向一端移动并更新引用,最后清理边界外内存,实现紧凑排列与连续空闲空间。

Java 老年代(Old Generation)在使用标记-整理(Mark-Compact)算法回收时,核心目标是解决内存碎片问题,同时安全地保留并移动存活对象。它不单纯清除,而是“先标、再清、最后挪”,让剩余对象紧凑排列,腾出连续大块空间。
标记阶段:识别哪些对象还活着
从 GC Roots(如线程栈帧中的引用、静态变量、JNI 引用等)出发,递归遍历所有可达对象,并在对象头或单独的位图中打上“存活”标记。这个过程与标记-清除算法一致,但后续行为不同——它不会直接释放死亡对象内存,而是为整理做准备。
整理阶段:把存活对象往一端移动
标记完成后,GC 线程会从堆内存的一端(比如起始地址)开始扫描,将所有标记过的存活对象依次复制或移动到另一端(如低地址侧),同时更新这些对象的引用地址。关键点包括:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 移动过程需修正所有指向该对象的引用(如栈中、其他对象字段、常量池等),JVM 通过写屏障或卡表辅助快速定位和更新
- 整理后,未被移动的区域变成一块连续的空闲空间,彻底消除碎片
- 移动操作本身有开销,所以 CMS 不用此算法(它用标记-清除),而 Serial Old、Parallel Old 默认采用标记-整理
实际触发与典型场景
老年代触发标记-整理,通常是因为以下情况之一:
- Minor GC 后,晋升对象无法放入老年代(空间不足或担保失败),触发 Full GC,此时若使用 Serial Old 或 Parallel Old,就会执行标记-整理
- 显式调用 System.gc()(不推荐),且 JVM 未禁用显式 GC,也可能触发
- 老年代空间使用率超过阈值(如 -XX:CMSInitiatingOccupancyFraction 对 CMS 无效,但对 G1 或 ZGC 不适用;而 Parallel GC 的 -XX:MaxGCPauseMillis 可能间接促使更激进的整理)
注意:不是所有老年代 GC 都用标记-整理
现代 JVM 中,算法选择取决于垃圾收集器:
- Serial Old / Parallel Old:默认使用标记-整理
- CMS:老年代用标记-清除(不整理),靠预留空间和 Concurrent Mode Failure 后的 Serial Old 回退来兜底
- G1:采用“标记-复制”思路,按 Region 混合回收,本质是整理,但粒度更细
- ZGC / Shenandoah:并发整理,移动对象时不暂停应用线程,但底层仍有标记+重定位逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










