Mark-Sweep是V8老生代核心回收算法,分标记(从根出发标记可达对象,支持增量以减少STW)和清除(释放未标记对象内存,不移动对象,易产生碎片)两步;碎片过多时由Mark-Compact补充整理。

Mark-Sweep(标记-清除)是 V8 引擎老生代内存回收的核心算法,它不靠“谁还用着我”来判断,而是靠“谁能找到我”——也就是可达性分析。只要一个对象能从根(如全局对象、栈中变量、DOM 节点等)出发,通过引用链被访问到,它就“活着”;否则,就是垃圾。
Mark-Sweep 分两步走:标记 + 清除
整个过程分两个明确阶段,中间没有重叠:
- 标记阶段:GC 从所有根对象开始,递归遍历所有可到达的对象,并给它们打上“存活”标记。这个过程会暂停 JavaScript 执行(Stop-The-World),但 V8 用增量标记把它拆成小块,穿插在 JS 任务之间,大幅缩短单次停顿时间。
- 清除阶段:GC 扫描整个老生代堆内存,把没被标记的对象所占的内存空间直接释放。注意:这一步不移动对象,只清空“空地”,所以容易产生内存碎片。
为什么老生代不用 Scavenge?
Scavenge(复制算法)适合新生代——空间小、对象短命、复制成本低。而老生代对象多、体积大、存活率高,如果硬用复制,就得搬动大量数据,效率极低。Mark-Sweep 不搬数据,只做逻辑判断和释放,更适合长期驻留的对象。
碎片多了怎么办?Mark-Compact 来补位
单纯 Mark-Sweep 清完后,内存里会留下东一块西一块的空隙,后续分配大对象时可能找不到连续空间。这时 V8 会在必要时触发Mark-Compact:先标记,再把所有存活对象往一端“挤”,腾出一大片连续空闲区,最后清理尾部。虽然耗时更长,但它解决的是内存利用率问题。
立即学习“Java免费学习笔记(深入)”;
实际中你该注意什么?
理解算法不是为了手写 GC,而是避开常见陷阱:
- 全局变量或闭包意外保留对大对象的引用,会让它一直“可达”,无法被 Mark-Sweep 清掉;
- 未解绑的事件监听器、没清除的定时器(
setInterval)、DOM 节点残留引用,都会让本该回收的对象继续挂在根链上; - 循环引用在现代 V8 中不会导致泄漏(因为不依赖引用计数),但若其中一环还被根连着,整条链就都活下来了。
本质上,Mark-Sweep 是一套基于可达性的自动化清道夫机制——它不猜你还要不要,只看你还能不能被找到。


















