v8引擎不采用引用计数作为主垃圾回收算法,因其无法解决循环引用导致的内存泄漏,且频繁更新计数器带来高开销;实际使用基于gc roots的可达性分析(如mark-sweep)配合分代回收。

引用计数是一种直观的垃圾回收思路:每个对象维护一个数字,记录有多少变量正指向它;一旦这个数字归零,就立刻回收内存。但它在 JavaScript 中已被淘汰,核心原因就是两个无法绕开的硬伤——循环引用导致的死锁,以及高频更新带来的性能拖累。
循环引用让引用计数“卡死”
当两个对象互相持有对方的引用时,它们的计数器永远无法降到 0:
- A 对象里有属性 a.ref = B,B 的计数 +1
- B 对象里有属性 b.ref = A,A 的计数 +1
- 即使整个函数执行完毕、外部再无任何变量访问 A 和 B,它们的计数仍各为 1
- 垃圾回收器只能干看着,不敢回收——因为“计数没归零”,误判为“还在用”
这种情形不是理论假设。DOM 元素绑定事件监听器、树节点父子互引、缓存 Map 存储对象及其回调,都极易触发。IE 浏览器曾长期受此困扰,页面停留越久,内存占用越高,最终崩溃。
每次赋值都要动计数器,开销真实存在
引用计数必须在运行时精确同步所有引用变化:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 变量赋新值、函数传参、作用域退出、闭包捕获……每一次都得加或减计数
- 多线程环境下还要加锁保护计数器,进一步拖慢执行
- 开发者稍不注意(比如异常中途退出、忘记置 null),某处减计数漏掉,整条对象链就永久滞留
这违背了 JavaScript “自动管理内存”的初衷,把本该由引擎兜底的事,悄悄转嫁给了人肉维护。
V8 等现代引擎怎么解决的
它们弃用了引用计数,改用可达性分析(Reachability Analysis):
- 从一组“根对象”出发(如全局对象、当前调用栈里的局部变量、活跃闭包)
- 顺着所有引用链“爬”一遍,能到达的对象标记为“活跃”
- 爬不到的对象,哪怕内部有复杂循环,也被判定为不可达,直接回收
这种方式天然无视循环引用——只要没人从根出发能找到它们,就等于“死了”。这也是为什么 today 的 JS 引擎不再因简单循环而泄漏内存。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










