标记-整理算法通过移动存活对象至内存一端并清理尾部空间,彻底消除内存碎片,形成连续空闲区;其标记阶段从gc roots出发标记可达对象,整理阶段紧凑迁移存活对象并更新所有引用,清除阶段一次性释放边界外内存。

标记-整理算法通过“移动存活对象 + 清理尾部空间”的方式,从物理布局上彻底消除内存碎片,让空闲内存变成一块连续区域,而不是一堆零散小块。
标记阶段:准确锁定哪些对象必须保留
从 GC Roots(如栈中变量、静态字段、JNI 引用等)出发,沿引用链遍历并标记所有可达对象。未被标记的即为垃圾,但此时不清理——这一步确保后续整理只动真正需要的对象,不遗漏也不误伤。
整理阶段:把存活对象统一“归位”到内存一端
所有已标记的存活对象(无论大小、类型)被按顺序紧凑地移动到内存起始端(通常是低地址侧)。这个过程不是简单压缩内容,而是整体平移对象内存块:
- 大对象(如巨型数组)保持完整,不拆分,只调整起始位置;
- 移动过程中同步更新所有指向它的引用,包括栈帧变量、其他对象字段、常量池条目等;
- 整理完成后,存活对象在内存中连续排列,中间无空洞。
清除阶段:一次性释放整段尾部空间
整理完成后,内存自然形成清晰分界:前端是紧凑的存活区,后端是连续的空闲区。GC 只需将分配指针(如 bump-the-pointer)重置到存活区末尾,之后所有新对象都可顺序分配,无需搜索或拼凑碎片。
这种结构直接解决大对象分配失败问题——比如需要 2MB 连续空间时,不再受限于“总空闲够但分散在10个200KB小块里”,而是能立刻拿到一块完整的 2MB 区域。
为什么它比标记-清除更抗碎片
标记-清除只是删掉垃圾,留下空洞,久而久之堆内存像筛子;标记-整理则主动“收拾房间”,把家具(存活对象)靠墙摆齐,腾出整面空地(空闲区)。虽然整理有开销(移动+修引用),但它换来的是长期稳定的连续分配能力,特别适合老年代这种高存活率、少变动、又常需容纳大对象的场景。











