7

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

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

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

buggyObject.callAgain();
buggyObject = null;

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

4

3 回答 3

7

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

  • 我将整个代码放在一个循环中,以创建相同的场景 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 在超时发生之前不会被释放。在我们的例子中,计时器会自行重置并永远运行,因此即使没有对原始对象的引用,它的内存空间也永远不会被收集。

于 2014-04-20T05:33:21.163 回答
5

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

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

于 2013-08-06T19:09:01.573 回答
1

正如 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...
于 2017-09-10T04:10:31.923 回答