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

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免费学习笔记(深入)”;
本文档主要讲述的是j2me3D游戏开发简单教程; 如今,3D图形几乎是任何一部游戏的关键部分,甚至一些应用程序也通过用3D形式来描述信息而获得了成功。如前文中所述,以立即模式和手工编码建立所有的3D对象的方式进行开发速度很慢且很复杂。应用程序中多边形的所有角点必须在数组中独立编码。在JSR 184中,这称为立即模式。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
关键事实:
- 新分配的对象进入
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 会隐式持有对参数对象的引用(尤其在 Chrome 和 Firefox 中),直到你关闭该行展开视图或刷新控制台。
这不是 bug,是调试工具的设计取舍。验证方式:
- 用
console.log(JSON.stringify(obj))替代,避免传入对象引用 - 在无控制台打开状态下运行代码,或用 Performance 面板录制内存分配,观察
Detached DOM tree或JS heap size变化 - 注意:即使你把变量设为
null,只要 DevTools 还“看着”它,V8 就不会回收
真正难处理的是那些看不见的引用链:DOM 节点 → 事件处理器 → 闭包 → 大数组。这类问题不会报错,只表现为内存缓慢上涨——得靠 Memory 面板的 “Allocation instrumentation on timeline” 才能定位源头。










