简体   繁体   English

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

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

I was reviewing the slides in this presentation: http://slid.es/gruizdevilla/memory我正在查看本演示文稿中的幻灯片: http : //slid.es/gruizdevilla/memory

and on one of the slides, this code is presented with the suggestion that it creates a memory leak:在其中一张幻灯片上,此代码显示了它会造成内存泄漏的建议:

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

buggyObject.callAgain();
buggyObject = null;

Can someone explain the issue in more detail here?有人可以在这里更详细地解释这个问题吗? I might be missing some subtleties here.我可能在这里遗漏了一些微妙之处。

This is definitely a memory leak.这绝对是内存泄漏。 However, the memory consumption is so small and cannot be measured.但是,内存消耗如此之小,无法衡量。 I did some small changes to the source code.我对源代码做了一些小的改动。

  • I put the entire code inside a loop to create the same scenario 100,000 times我将整个代码放在一个循环中以创建相同的场景 100,000 次
  • I increased the timer interval to about 16 minutes.我将计时器间隔增加到大约 16 分钟。 This prevents browser from crashing这可以防止浏览器崩溃

Here is the code:这是代码:

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 内存泄漏

My experiment:我的实验:

I ran the code in Chrome Version 34.0.1847.116 m and captured memory changes using Developer Tools\\Timeline.我在Chrome 版本 34.0.1847.116 m 中运行代码并使用 Developer Tools\\Timeline 捕获内存更改。

As we can see in the picture, around 32 MB of memory has been consumed for running this code and after a while it's been decreased to about 30 MB and stayed unchanged (see #1).正如我们在图片中看到的,运行这段代码消耗了大约 32 MB 的内存,一段时间后它减少到大约 30 MB 并保持不变(参见 #1)。 After several garbage collecting attempts by Chrome (see #2) and one manual forced garbage collection by me (see #3, #4), the memory consumption remains unchanged.在 Chrome 尝试多次垃圾回收(参见 #2)和我手动强制垃圾回收(参见 #3、#4)之后,内存消耗保持不变。 There is no more buggyObject and there is nothing we can do to release the memory.不再有buggyObject并且我们无法释放内存。 The only possible way is to close the browser.唯一可能的方法是关闭浏览器。

What causes this?这是什么原因造成的?

The main reason for this behavior is the timer.这种行为的主要原因是计时器。 Timer callback and its tied object, buggyObject will not be released until the timeout happens.计时器回调及其绑定对象,直到超时发生才会释放 buggyObject。 In our case timer resets itself and runs forever and therefore its memory space will never be collected even if there is no reference to the original object.在我们的例子中,定时器会重置自己并永远运行,因此即使没有对原始对象的引用,它的内存空间也永远不会被回收。

There is another question that describes how setTimeout() looks like it has memory leaks, but in reality does not.还有另一个问题描述了 setTimeout() 看起来如何有内存泄漏,但实际上并没有。

However, I think what the author is trying to say is that since buggyObject creates a setTimeout which calls itself, even if you change buggyObject to equal null (saying you are done with the object and it can be cleaned up), the object won't be garbage collected because there is still a reference to it in the setTimeout().但是,我认为作者想说的是,由于buggyObject创建了一个调用自身的 setTimeout,即使您将 buggyObject 更改为等于null (说您已完成该对象并且可以对其进行清理),该对象也不会'不会被垃圾收集,因为在 setTimeout() 中仍然有对它的引用。 This is technically a memory leak because there is no longer any direct reference to the setTimeout function so that you could clear it later (kind of a zombie timeout if you will).这在技术上是内存泄漏,因为不再有对 setTimeout 函数的任何直接引用,因此您可以稍后清除它(如果您愿意的话,这有点像僵尸超时)。

As advncd pointed out, the timer gets executed and adds even more data on the stack.正如advncd 指出的那样,计时器被执行并在堆栈上添加更多数据。 There is a conceptual view of what happens:对发生的事情有一个概念性的看法:

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...

So each time a new variable a is allocated on the stack which grows forever.因此,每次在永远增长的堆栈上分配一个新变量a

What advncd did not mention, though, is the fact that you have a setInterval() function to do what you need to do: have the same function called over and over again.然而,advncd 没有提到的是,您有一个 setInterval() 函数来完成您需要做的事情:一遍又一遍地调用相同的函数。 Now you still have a "memory leak" but only the initialization parameters leak (ie it won't grow each time the timer times out.)现在你仍然有一个“内存泄漏”,但只有初始化参数泄漏(即它不会在每次计时器超时时增长。)

So conceptually, the calls are flat and you avoid the leak:所以从概念上讲,调用是平坦的,您可以避免泄漏:

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