javascript内存回收的核心标准是“是否可达”,即从根(如全局对象、调用栈变量)出发能否通过引用链访问到对象;可达则存活,不可达则回收,现代引擎采用标记-清除算法解决循环引用问题。

JavaScript 内存管理中判断一个对象是否该被回收,核心在于“是否可达”,而不是“有没有变量名指向它”。这个判断标准由垃圾回收器(GC)执行,不同算法逻辑略有差异,但本质一致:只有从根(root)出发能访问到的对象才被视为“在使用”。
可达性是根本标准
所谓“可达”,是指从全局对象(浏览器中是 window 或 globalThis,Node.js 中是 global)、当前执行上下文的活动变量、调用栈中的局部变量等“根”出发,通过引用链能一路访问到的对象。只要存在一条引用路径,该对象就不可回收。
- 即使某个对象没有被任何变量直接命名,只要它被另一个可达对象所引用,它就依然存活
- 函数参数、闭包内部捕获的变量、事件监听器绑定的对象,都可能构成隐式引用链
- DOM 元素一旦从文档树移除,若 JS 中仍有变量引用它,它仍可达;反之,若无任何引用,即便还在页面上(如未正确清理事件),也可能被回收(取决于具体引擎实现和 DOM 生命周期)
引用计数曾用但已弃用
早期(如 IE8 及更早)曾用引用计数算法:每个对象维护一个引用计数,被赋值一次就 +1,被赋新值或离开作用域就 -1,归零即回收。但它无法处理循环引用问题:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 两个对象互相引用(如
a.b = b; b.a = a),即使外部再无引用,计数始终 ≥1,导致内存泄漏 - DOM 与 JS 对象间的循环引用(如
element.obj = obj; obj.el = element)曾是典型场景 - 现代引擎(V8、SpiderMonkey 等)已完全放弃该算法作为主 GC 方案
标记-清除是主流实现
当前所有主流 JavaScript 引擎均采用基于可达性的标记-清除(Mark-and-Sweep)或其变种(如 V8 的分代式 GC)。它的判断流程分两步:
- 标记阶段:从根开始遍历所有引用,递归标记所有能到达的对象
- 清除阶段:扫描堆内存,回收所有未被标记的对象所占空间
- 循环引用不会干扰标记过程——只要整个环路无法从根到达,整组对象都会被一并标记为不可达并回收
开发者需关注的引用来源
实际编码中,真正影响可达性的常见引用包括:
- 全局变量(含隐式全局,如未声明的赋值)
- 闭包中被捕获的自由变量
- 定时器回调(
setInterval/setTimeout)持有的上下文 - 事件监听器(尤其是未移除的
addEventListener)及其回调闭包 - WeakMap / WeakSet 中的键不计入强引用,但 Map/Set 会阻止键和值被回收
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










