标记整理算法主要用于老年代,Serial Old和Parallel Old收集器默认采用该算法,通过标记存活对象并将其向一端移动来消除内存碎片,ZGC与Shenandoah则通过并发转移实现等效整理。

Java 中老年代默认不使用标记整理算法,而是采用标记清除或 G1 的混合回收策略;若想在老年代启用标记整理(即压缩),需选择支持该行为的垃圾收集器,并合理配置。
哪些 GC 支持老年代标记整理
标记整理(Mark-Compact)的核心是移动存活对象、消除碎片。Java 中以下收集器可在老年代执行整理:
- Serial Old:单线程,老年代默认使用标记整理,适合小内存、低延迟要求场景
- Parallel Old:多线程版本,JDK 8+ 默认开启老年代压缩(通过 -XX:+UseParallelOldGC 启用,自动整理)
- ZGC / Shenandoah:虽然不叫“标记整理”,但通过读屏障+并发转移实现无碎片回收,效果等价于持续整理
- G1:默认不对整个老年代做整理,仅对部分回收集(Collection Set)中的区域进行复制(即局部整理),可通过 -XX:G1HeapRegionSize 和 -XX:G1MixedGCCountTarget 影响整理强度
如何显式启用老年代压缩
以 Parallel GC 为例(最直接支持老年代全局整理):
- 启用 Parallel Old:-XX:+UseParallelGC -XX:+UseParallelOldGC(JDK 8+ 后者默认开启)
- 强制每次 Full GC 整理老年代:-XX:+UseAdaptiveSizePolicy 配合 -XX:MaxGCPauseMillis 可间接促使压缩更频繁
- 禁用触发阈值导致的非整理行为:避免设置 -XX:-UseAdaptiveSizePolicy 后手动调参失当,造成老年代碎片累积
注意:Serial Old 无需额外参数,只要用 -XX:+UseSerialGC,其老年代收集器 Serial Old 就天然整理。
立即学习“Java免费学习笔记(深入)”;
避免误用:标记清除 ≠ 标记整理
CMS(已废弃)和部分 G1 配置下,老年代回收可能退化为标记清除(Mark-Sweep),只清理不移动,易产生碎片:
- CMS 不支持整理,老年代碎片严重时会触发 Serial Old 进行 Full GC(此时才整理),但停顿长
- G1 若 Mixed GC 回收效率低,可能转为 Full GC(使用单线程标记整理),这是退化行为,应通过调优避免
- 可通过 GC 日志识别是否发生整理:PSYoungGen / ParOldGen 区域大小变化连续、且 after ≤ before,说明发生了压缩;若老年代 used 持续增长但 committed 不增,大概率有碎片
实用建议:何时需要关注老年代整理
- 应用长期运行后出现频繁 Full GC,且每次回收后老年代占用率下降不明显 → 检查碎片
- 分配大对象(如 > 512KB 数组)频繁失败,抛 java.lang.OutOfMemoryError: GC overhead limit exceeded 或直接 OOM → 很可能是碎片导致无法找到连续空间
- 选用 Parallel GC 时,配合 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 观察 ParOldGen 行的 “before/after” 值,确认整理生效
- 生产环境优先考虑 ZGC 或 Shenandoah(JDK 11+),它们在低延迟前提下自动解决碎片问题,无需手动干预整理逻辑


















