javascript垃圾回收主要依靠标记清除算法自动管理内存,引擎周期性从根对象出发标记可达对象,清除未标记对象;v8采用分代回收优化性能,但全局变量、未清理定时器、闭包引用等易导致内存泄漏。

JavaScript 的垃圾回收主要靠标记清除(Mark-and-Sweep)算法自动管理内存,它不依赖开发者手动释放,而是由引擎周期性地识别并回收不再被引用的对象。
标记阶段:从根对象出发遍历可达对象
引擎从一组“根”(如全局对象、当前执行函数的局部变量、调用栈中的引用等)开始,递归追踪所有能被直接或间接访问到的对象,并给它们打上“存活”标记。无法从根到达的对象不会被标记。
- 比如一个对象被某个变量引用,而该变量在当前作用域中活跃,这个对象就会被标记
- 闭包中捕获的外部变量,只要闭包本身还被引用,其捕获的变量也会被标记
- DOM 元素被 JS 变量引用时,也会被标记;但如果 JS 引用被清除,且该元素也从 DOM 树移除,就可能进入待回收队列
清除阶段:回收未被标记的对象
标记完成后,垃圾回收器扫描堆内存,把所有未被标记的对象所占的内存空间释放掉,并将其内存块归入空闲列表,供后续 new 或 {} 等操作复用。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 这个过程不是立即发生,通常在内存压力较大或引擎认为合适时机触发
- V8 引擎会分代收集:新生代用“Scavenge”算法快速回收短命对象,老生代才主要用标记清除
- 多次经历垃圾回收仍存活的对象会被晋升到老生代,减少频繁标记开销
什么情况会导致对象无法被回收?
即使逻辑上不再需要,若仍有隐式引用存在,对象就无法被标记清除,造成内存泄漏。
- 全局变量意外保留了对大对象的引用(例如 window.cache = hugeData)
- 事件监听器未移除,且回调中闭包引用了外部大对象
- 定时器(setInterval)中持续持有对象引用,又忘记 clearInterval
- DOM 节点被移除但 JS 仍保留对其引用,尤其在使用缓存映射时容易忽略清理
如何辅助标记清除更高效工作?
虽然不能直接控制 GC,但可以减少干扰,帮助引擎更快识别可回收对象。
- 及时将不再需要的大对象引用设为 null(尤其在 long-lived 作用域中)
- 使用 WeakMap / WeakSet 存储关联数据——它们的键是弱引用,不影响标记结果
- 避免在闭包中无意延长对象生命周期,比如只传需要的字段而非整个对象
- 开发中可用 Chrome DevTools 的 Memory 面板拍堆快照,对比差异定位泄漏点
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










