我正在审核此演示文稿中的幻灯片:http://slid.es/gruizdevilla/memory
并在其中一张幻灯片上显示此代码,建议它会造成内存泄漏:
var buggyObject = {
callAgain: function() {
var ref = this;
var val = setTimeout(function() {
ref.callAgain();
}, 1000);
}
}
buggyObject.callAgain();
buggyObject = null;
有人可以在这里更详细地解释这个问题吗?我可能会在这里遗漏一些细微之处。
答案 0 :(得分:6)
这绝对是内存泄漏。但是,内存消耗很小,无法测量。我对源代码做了一些小改动。
以下是代码:
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版本34.0.1847.116 m 中运行了代码,并使用Developer Tools \ Timeline捕获了内存更改。
正如我们在图片中看到的,运行此代码消耗了大约32 MB的内存,一段时间后它已经减少到大约30 MB并保持不变(参见#1)。
经过Chrome的几次垃圾收集尝试(参见#2)和我的一次手动强制垃圾收集(参见#3,#4),内存消耗保持不变。
没有buggyObject
,我们无法释放内存。唯一可行的方法是关闭浏览器。
是什么导致这种情况?
此行为的主要原因是计时器。定时器回调及其绑定对象,buggyObject在超时发生之前不会被重新启动。在我们的情况下,计时器会自行重置并永久运行,因此即使没有对原始对象的引用,也不会收集其内存空间。
答案 1 :(得分:5)
有another question描述了setTimeout()看起来有内存泄漏的方式,但实际上并没有。
但是,我认为作者试图说的是,因为buggyObject
创建了一个调用自身的setTimeout,即使你将buggyObject更改为等于null
(说你完成了对象和它可以被清理),对象不会被垃圾收集,因为在setTimeout()中仍然有对它的引用。这在技术上是一个内存泄漏,因为不再有任何对setTimeout函数的直接引用,以便您以后可以清除它(如果你愿意,可以使用zombie超时)。
答案 2 :(得分:0)
正如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()函数来做你需要做的事情:一遍又一遍地调用相同的函数。现在你仍然有一个&#34;内存泄漏&#34;但只有初始化参数泄漏(即每次定时器超时,它都不会增长。)
从概念上讲,这些电话是平的,你可以避免泄密:
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...