[英]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.
我对源代码做了一些小的改动。
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;
}
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.