You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS闭包内存泄漏原理:两段相似代码内存表现差异问询

核心前提:现代JS引擎的闭包优化逻辑

现代主流JS引擎(比如Chrome的V8)对闭包的变量捕获做了精确优化:

  • 单个闭包只会捕获自己实际用到的父作用域变量,不会保留整个作用域的所有变量
  • 同一个父作用域下生成的所有闭包,共享同一个词法环境对象,只要其中任意一个闭包引用了某个父作用域变量,这个变量就会被保留在共享环境中,所有同作用域闭包都能访问到它

第一段代码无内存泄漏的原因

var theThing = new Array(1000000000);
var replaceThing = function () {
  var originalThing = theThing;
 
  var unCalled = function () {
    if (originalThing)
      return originalThing
  };
  var obj = {
    longStr: new Array(1000000).join('*'),
    someMethod: function () {}
  };
};
setInterval(replaceThing, 1000);

这段代码执行完replaceThing函数后,作用域内生成的unCalled、obj两个变量都是纯局部变量,没有被任何外部作用域(比如全局作用域)持有引用。函数执行结束后,整个函数作用域的所有内容包括originalThing、两个闭包、obj对象都会被垃圾回收器直接回收,自然不会出现内存泄漏。


第二段代码出现内存泄漏的原因

var theThing = new Array(1000000000);
var replaceThing = function () {
  var originalThing = theThing;
 
  var unused = function () {
    if (originalThing)
      console.log("Hi")
  };
   theThing = {
    longStr: new Array(1000000).join('*'),
     someMethod: function () {}
  };
 
};
setInterval(replaceThing, 1000);

这段代码和第一段的核心差异有两点:

  1. 你把新创建的包含someMethod的对象直接赋值给了全局变量theThing,导致这个对象和内部的someMethod闭包永远被全局持有,不会被回收
  2. 同作用域下的unused闭包引用了originalThing,根据共享词法环境的规则,originalThing会被保留在闭包共享环境中。而someMethod和unused属于同一个父作用域,共享同一个环境,哪怕someMethod本身没有用到originalThing,这个变量也不会被回收。

每次replaceThing执行时,都会把上一轮的全局theThing赋值给originalThing,再生成新的theThing持有新的someMethod闭包,新的闭包又绑定着存了上一轮大数组的originalThing,最终形成链式的引用链,所有历史的大数组都无法被回收,就出现了内存泄漏。


内容的提问来源于stack exchange,提问作者chen.w

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 21:09:02