java垃圾回收中处理内存碎片化的核心思路是通过标记-整理算法将存活对象紧凑移至一端,或通过复制算法整体搬迁存活对象,从而消除不连续空闲块;前者用于老年代,后者用于新生代,分代配合实现碎片可控。

Java 垃圾回收中处理内存碎片化问题,核心思路是避免“标记-清除”后留下的不连续空闲块,从而保障大对象分配顺利、减少频繁 GC。整理阶段正是为解决这一问题而设计的关键环节。
标记-整理算法(Mark-Compact)直接应对碎片
该算法在标记存活对象后,不直接清除,而是将所有存活对象向内存一端(如起始地址)紧凑移动,再统一清理边界之后的全部空间。
- 移动过程会更新所有指向这些对象的引用(如栈帧、静态变量中的指针),确保程序逻辑不受影响
- 整理后堆内存呈现“存活区 + 空闲区”的连续布局,彻底消除碎片
- 适合老年代这类对象生命周期长、回收频率低但空间宝贵的区域
复制算法(Copying)从源头规避碎片
它不依赖“清理残留”,而是把存活对象整体搬迁到另一块预先准备好的空闲区域,原区域直接整块清空。
- 实际 JVM(如 HotSpot)中,年轻代采用 Eden + 两个 Survivor 区的设计,就是复制算法的工程化实现
- 每次回收只需关注当前使用中的半区,复制后角色互换,天然无碎片
- 代价是需预留双倍空间容量,因此主要用于对象朝生夕死、存活率低的年轻代
分代收集配合策略性整理
现代 JVM 不单独依赖某一种算法,而是按对象年龄分代,动态组合使用:
- 年轻代:以复制为主(如 ParNew、G1 的 Young GC),高效且无碎片
- 老年代:CMS 曾尝试用标记-清除(牺牲碎片换低停顿),但最终被 G1、ZGC、Shenandoah 取代;这些新型收集器在老年代引入“局部整理”或“并发压缩”,在低延迟前提下控制碎片增长
- G1 的混合回收(Mixed GC)会选定若干回收集(Collection Set),对其中的分区(Region)进行复制或整理,实现碎片可控
触发整理的典型场景
不是每次 GC 都执行整理,JVM 会根据碎片程度和回收目标权衡:
- 老年代空间使用率超过阈值(如 -XX:InitiatingOccupancyPercent),且预测下次分配可能失败时,会启动带整理的 Full GC 或混合回收
- Serial 和 Parallel Old 收集器默认在 Full GC 时执行完整整理
- G1 在 Evacuation 失败(to-space overflow)后,可能退化为单线程 Full GC 并强制整理整个堆
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











