标记-清除算法先标记所有可达对象,再清除未标记垃圾;优点是解决循环引用、实现简单、不移动对象;缺点是内存碎片严重、遍历全堆导致效率低且停顿不可控。

标记清除算法通过两个明确阶段完成垃圾回收:先标记所有可达对象,再统一清理未被标记的对象。它不依赖引用计数,能准确识别并回收循环引用的垃圾对象,是 JVM 等主流运行时的基础 GC 机制之一。
执行过程分两步走
整个流程严格分为标记(Mark)和清除(Sweep)两个阶段,中间无重叠:
- 标记阶段:从 GC Roots(如栈帧中的局部变量、静态字段、JNI 引用等)出发,递归遍历所有可达对象,在对象头(Header)中打上“存活”标记。这个过程采用深度优先或广度优先搜索,确保所有能被访问到的对象都被覆盖。
- 清除阶段:线性扫描整个堆内存,对每个对象检查其标记状态;未被标记的对象即判定为垃圾,直接释放其所占内存,并将该空间加入空闲链表,供后续分配使用;同时清除存活对象的标记位,为下一轮 GC 做准备。
核心优点:简单可靠,解决关键痛点
该算法在设计上直击早期内存管理的硬伤:
- 能正确处理循环引用——比如 A 引用 B、B 又引用 A,但二者均已脱离 GC Roots 的可达路径,标记清除仍可识别并回收,这是引用计数无法做到的。
- 实现简单,逻辑清晰,无需移动对象,避免了复制或整理带来的额外开销与复杂性。
- 对存活对象零干扰,不改变对象内存地址,适合对指针稳定性有要求的场景。
主要缺点:碎片与效率瓶颈
实际落地时,两大问题长期制约其性能表现:
- 内存碎片化严重:回收后留下大量不连续的小块空闲空间。例如,一个需占用 3 个连续内存单元的大对象,可能因碎片分散而无法分配,被迫触发额外 GC,甚至引发 OOM。
- 执行效率随垃圾量增长而下降:无论堆中只有 10% 还是 90% 是垃圾,算法都必须完整遍历整个堆做标记和清除,导致停顿时间(Stop-the-World)不可控,尤其在大堆场景下明显拖慢应用响应。
- 空闲空间管理依赖链表查找,分配速度变慢——每次申请内存需遍历空闲链表匹配合适大小,不如连续分配高效。
为什么还在用?现实中的定位
尽管有缺陷,标记清除并未被淘汰,而是被合理嵌入分代收集策略中:
- 常用于老年代回收,因为老年代对象存活率高,复制成本太大,而碎片问题可通过定期触发标记-整理(Mark-Compact)缓解。
- 作为其他算法(如复制、标记-整理)的理论基础和对照基准,帮助理解 GC 设计取舍。
- 部分轻量级或嵌入式运行环境仍直接采用,因其代码体积小、资源消耗低。

















