JavaScript垃圾回收由引擎自动管理,采用标记-清除算法,结合分代收集与增量/并发标记;常见内存泄漏包括闭包不当引用、全局变量、未清理定时器及DOM引用未解除。

JavaScript 的垃圾回收(Garbage Collection, GC)由引擎自动管理,开发者无需手动释放内存。它主要通过标记-清除(Mark-and-Sweep)算法工作:先标记所有可达对象(从全局对象、当前执行上下文等根出发),再清除未被标记的不可达对象。现代引擎(如 V8)还会结合分代收集(将对象按生命周期分为新生代和老生代)和增量/并发标记来提升性能,减少卡顿。
闭包不当引用导致的内存泄漏
闭包会维持对外部作用域变量的引用。如果本该被释放的变量被闭包长期持有,就无法被回收。
- 常见场景:事件监听器中使用闭包保存大对象,且未及时移除监听器
- 错误示例:const data = new Array(1000000).fill('leak'); const handler = () => console.log(data.length); element.addEventListener('click', handler); —— 即使 element 被移除,data 仍因闭包被引用而驻留
- 建议:避免在闭包中引用大型数据;监听器用完后调用 removeEventListener;或改用一次性监听(addEventListener(..., { once: true }))
全局变量与意外挂载
显式或隐式创建的全局变量永远不会被回收,是低级但高频的泄漏源。
- 隐式全局:忘记写 let/const/var,直接赋值(myData = {...})
- 意外挂载:给 window 或 globalThis 动态添加属性(window.cache = largeObj)
- 建议:启用严格模式('use strict')防止隐式全局;避免直接向全局对象写入缓存;用模块级变量替代全局缓存
定时器与回调函数持有引用
未清理的 setInterval、setTimeout 或 Promise 链中的闭包,可能持续引用 DOM 元素或数据对象。
立即学习“Java免费学习笔记(深入)”;
- 典型问题:组件销毁后,定时器仍在运行并引用已卸载的 DOM 或 this 实例
- 错误示例:this.timer = setInterval(() => this.update(this.data), 1000); —— 组件卸载时未调用 clearInterval(this.timer)
- 建议:组件生命周期结束时清除所有定时器;用 WeakMap 存储关联数据,避免强引用;考虑用 AbortController 控制异步操作生命周期
DOM 引用未解除
DOM 节点被 JavaScript 对象引用时,即使从文档中移除,只要引用存在,整个节点及其子树都无法回收。











