带闭包的 JavaScript 垃圾收集

Ben*_*Ben 1 javascript

什么时候可以o回收垃圾?我假设完成后setTimeout回调?

var o = {
    foo() {
        setTimeout(function() {}, 10000);
    }
};

o.foo();
o = null;
Run Code Online (Sandbox Code Playgroud)

和这个?

var o = {
    bar: 0
};

o.foo = function() {
    setTimeout(function() { o.bar; }, 10000);
}

o.foo(); // Sorry missed this important bit.
o = null;
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 5

(旁注:对于任何想知道 Ben 的代码在语法上是否正确的人:是的,它是,从 ES2015 开始;它不会在 ES5 或更早版本中。它使用了ES2015 为对象初始值设定项定义的新“方法定义”语法。)

关于这个代码,你的第一个问题是:

var o = {
    foo() {
        setTimeout(function() {}, 10000);
    }
};

o.foo();
o = null;
Run Code Online (Sandbox Code Playgroud)

最初分配给的对象o可以立即进行 GC,甚至在计时器回调之前,因为在o = null;语句之后没有保留对它的引用。

但是还有其他对象可能适合也可能不适合 GC,这取决于理论(规范)与实践(规范不排除实现优化):

在定时器回调发生之前,定时器机制保持对匿名函数的引用,因此匿名函数不符合 GC 的条件。

理论上,匿名函数具有对创建它的环境对象的引用,而环境对象又具有对 的引用foo,因此匿名函数会同时保留环境和foo内存,直到定时器触发、释放其引用,并且匿名函数有资格进行 GC。

在实践中,由于匿名函数不引用foo或其中的任何参数或局部变量foo(并且不使用eval),JavaScript 实现可以自由优化并允许foo该环境对象被 GC 处理,即使setTimeout调用仍然具有对该匿名函数的引用。(有些确实做了闭包优化,比如 V8。)他们可以这样做,因为除了内存消耗或缺乏内存之外,我们看不到任何副作用。


关于此代码,请回复您的其他问题:

var o = {
    bar: 0,
    foo() {
        setTimeout(function() { o.bar; }, 10000);
  }
};

o.foo();
o = null;
Run Code Online (Sandbox Code Playgroud)

对象本身仍然可以立即进行 GC。

Refoo和调用它的环境对象:

理论上,它们都被保留了(如上所示)。在实践中,我不知道这是否会影响引擎可以做的优化foo以及调用的环境。它不应该,因为o变量绑定到全局环境,而不是通过调用 设置的变量foo,因此可以编辑环境链以跳过调用创建的环境foo。因此引擎可以对其进行优化,并且完全猜测我会说他们可能会这样做,但我不知道他们会这样做。