繁体   English   中英

setTimeout() 如何在这段代码中造成内存泄漏?

[英]How does setTimeout() create a memory leak in this code?

我正在查看本演示文稿中的幻灯片: http : //slid.es/gruizdevilla/memory

在其中一张幻灯片上,此代码显示了它会造成内存泄漏的建议:

var buggyObject = {
   callAgain: function() {
     var ref = this;
     var val = setTimeout(function() {
        ref.callAgain();
     }, 1000);
   }
}

buggyObject.callAgain();
buggyObject = null;

有人可以在这里更详细地解释这个问题吗? 我可能在这里遗漏了一些微妙之处。

这绝对是内存泄漏。 但是,内存消耗如此之小,无法衡量。 我对源代码做了一些小的改动。

  • 我将整个代码放在一个循环中以创建相同的场景 100,000 次
  • 我将计时器间隔增加到大约 16 分钟。 这可以防止浏览器崩溃

这是代码:

for (var i = 0; i < 100000; i++) {
    var buggyObject = {
       callAgain: function() {
         var ref = this;
         var val = setTimeout(function() {
            ref.callAgain(); 
         }, 1000000); //16m
       }
    }

    buggyObject.callAgain();
    buggyObject = null;
}

Chrome 内存泄漏

我的实验:

我在Chrome 版本 34.0.1847.116 m 中运行代码并使用 Developer Tools\\Timeline 捕获内存更改。

正如我们在图片中看到的,运行这段代码消耗了大约 32 MB 的内存,一段时间后它减少到大约 30 MB 并保持不变(参见 #1)。 在 Chrome 尝试多次垃圾回收(参见 #2)和我手动强制垃圾回收(参见 #3、#4)之后,内存消耗保持不变。 不再有buggyObject并且我们无法释放内存。 唯一可能的方法是关闭浏览器。

这是什么原因造成的?

这种行为的主要原因是计时器。 计时器回调及其绑定对象,直到超时发生才会释放 buggyObject。 在我们的例子中,定时器会重置自己并永远运行,因此即使没有对原始对象的引用,它的内存空间也永远不会被回收。

还有另一个问题描述了 setTimeout() 看起来如何有内存泄漏,但实际上并没有。

但是,我认为作者想说的是,由于buggyObject创建了一个调用自身的 setTimeout,即使您将 buggyObject 更改为等于null (说您已完成该对象并且可以对其进行清理),该对象也不会'不会被垃圾收集,因为在 setTimeout() 中仍然有对它的引用。 这在技术上是内存泄漏,因为不再有对 setTimeout 函数的任何直接引用,因此您可以稍后清除它(如果您愿意的话,这有点像僵尸超时)。

正如advncd 指出的那样,计时器被执行并在堆栈上添加更多数据。 对发生的事情有一个概念性的看法:

var a = 123;
// call the setTimeout.function
  var a = 123;
  // call the setTimeout.function
    var a = 123;
    // call the setTimeout.function
      var a = 123;
      // call the setTimeout.function
        var a = 123;
        // call the setTimeout.function
          var a = 123;
          // call the setTimeout.function
            var a = 123;
            // call the setTimeout.function
              var a = 123;
              // call the setTimeout.function
                var a = 123;
                ...etc...

因此,每次在永远增长的堆栈上分配一个新变量a

然而,advncd 没有提到的是,您有一个 setInterval() 函数来完成您需要做的事情:一遍又一遍地调用相同的函数。 现在你仍然有一个“内存泄漏”,但只有初始化参数泄漏(即它不会在每次计时器超时时增长。)

所以从概念上讲,调用是平坦的,您可以避免泄漏:

a = 123;
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
...etc...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM