javascript中的垃圾回收是如何进行的【教程】

2次阅读

javaScript垃圾回收由引擎自动执行,基于可达性判断对象是否存活;V8采用分代式GC(Young代用Scavenge,Old代用Mark-Sweep-Compact)与增量标记;DevTools的console.log会隐式持有对象引用阻碍回收。

javascript中的垃圾回收是如何进行的【教程】

javascript 的垃圾回收不是你手动触发的,而是引擎自动运行的;它不保证立刻回收,也不暴露控制接口——你只能通过理解机制来间接影响内存行为。

哪些值会被自动标记为“可回收”

引擎用“可达性(reachability)”判断对象是否存活:从根(如全局对象、当前执行上下文中的局部变量、调用里的引用)出发,能顺着引用链访问到的值,就是“可达”的;其余都被视为垃圾。

常见误判场景:

  • 闭包中意外保留对外部大对象的引用,比如 function createHandler() { const bigData = new Array(1000000); return () => console.log(bigData.Length); } —— 即使 bigData 在逻辑上已无用,闭包仍让它持续存活
  • 事件监听器未移除,且监听函数捕获了大对象,比如 el.addEventListener('click', () => doSomething(largeObj)),而 el 长期存在
  • 定时器回调引用外部作用域变量,setInterval(() => console.log(cache), 1000)cache 无法释放

V8 中主要使用什么回收算法

V8 同时采用两种策略:分代式垃圾回收(Generational GC) + 增量标记(Incremental Marking),不是单一算法在跑。

立即学习Java免费学习笔记(深入)”;

关键事实:

  • 新分配的对象进入 Young Generation(又分 Scavenge 算法的 From/To 空间),存活两次后晋升到 Old Generation
  • Old Generation 使用 Mark-Sweep-Compact:先标记所有可达对象,再清除未标记的,最后可选压缩内存(避免碎片)
  • 标记阶段被拆成小块(增量),避免单次停顿过长;但整个周期仍可能跨多个 js 任务(microtask/macrotask)
  • global.gc() 仅在 node.js 开启 --expose-gc 时可用,浏览器环境完全不可调用

为什么 console.log(obj) 后 obj 还没被回收

DevTools 控制台的 console.log 会隐式持有对参数对象的引用(尤其在 chromefirefox 中),直到你关闭该行展开视图或刷新控制台。

这不是 bug,是调试工具的设计取舍。验证方式:

  • console.log(json.stringify(obj)) 替代,避免传入对象引用
  • 在无控制台打开状态下运行代码,或用 Performance 面板录制内存分配,观察 Detached dom treeJS heap size 变化
  • 注意:即使你把变量设为 NULL,只要 DevTools 还“看着”它,V8 就不会回收

真正难处理的是那些看不见的引用链:DOM 节点 → 事件处理器 → 闭包 → 大数组。这类问题不会报错,只表现为内存缓慢上涨——得靠 Memory 面板的 “Allocation instrumentation on timeline” 才能定位源头。

text=ZqhQzanResources