登录
首页 >  文章 >  前端

JavaScript对象引用计数与垃圾回收原理

时间:2026-03-26 16:33:43 292浏览 收藏

JavaScript的垃圾回收机制摒弃了易导致内存泄漏的引用计数法,转而采用更可靠的标记-清除算法——从全局对象、调用栈等根节点出发,递归标记所有可达对象,未被标记的即被自动回收;尤其值得注意的是,循环引用(如对象互引或JS-DOM交叉引用)在引用计数下会“锁死”内存,而现代引擎(如V8)已彻底解决这一顽疾;开发者虽无需手动管理内存,但可通过及时解绑事件监听器、清除定时器、避免隐式全局变量及善用WeakMap/WeakSet等弱引用结构,主动协助GC高效运作,从而写出更健壮、低内存泄漏风险的代码。

JavaScript中对象生命周期与垃圾回收的引用计数

JavaScript中对象的生命周期由引擎自动管理,垃圾回收(GC)主要采用标记-清除算法,而非引用计数。

为什么JavaScript不使用引用计数?

引用计数存在根本性缺陷:无法处理循环引用。即使两个对象彼此引用但已脱离全局作用域,它们的引用计数仍大于0,导致内存无法释放。

  • 例如:let objA = {}; let objB = {}; objA.ref = objB; objB.ref = objA; —— 此时objA和objB互相持有引用,但若再无其他变量指向它们,逻辑上应被回收,而引用计数算法会遗漏它们
  • 早期IE6–8在DOM/BOM对象上曾用引用计数,因此出现过因循环引用(如JS对象引用DOM,DOM又回调JS)导致的内存泄漏问题;现代浏览器已统一采用更可靠的标记-清除机制

V8引擎如何判断对象是否“可达”?

GC从一组称为“根(roots)”的活跃对象出发(如全局对象、当前执行函数的局部变量、调用栈中的值),递归标记所有能访问到的对象。未被标记的对象即为不可达,随后被清除。

  • 函数执行结束时,其局部作用域内声明的对象若未被外部闭包捕获,通常在下一次GC中被回收
  • 全局变量、定时器回调、事件监听器中保存的引用,都可能意外延长对象生命周期
  • WeakMap和WeakSet的键是弱引用,不计入标记路径,因此不影响GC——这是避免内存泄漏的重要工具

开发者能做什么来协助垃圾回收?

虽然不用手动释放内存,但可主动切断不必要的引用,让对象尽早变为不可达。

  • 及时解除事件监听:element.removeEventListener('click', handler),尤其在组件卸载或对象销毁时
  • 清空不再需要的定时器:clearTimeout(timerId)clearInterval(intervalId)
  • 避免意外的全局引用:foo = {}(漏写var/let/const)会挂到window上,长期驻留
  • 大对象(如缓存、图像数据)使用完毕后,显式赋值为null(仅在必要场景,如长生命周期对象中持有短生命周期大数据)

到这里,我们也就讲完了《JavaScript对象引用计数与垃圾回收原理》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于的知识点!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>