我试图理解为什么以下代码会导致内存泄漏
var aThing = null;
var outer = function() {
console.log('running');
var something = aThing;
var closure1 = function() {
if (something) {
console.log('something');
}
};
aThing = {
str: new Array(1000000).join('8'),
someMethod: function() {}
};
};
setInterval(outer, 1000);
以下是显示Google Chrome内存增加的时间表:
但是这个代码是一个非常小的变化不会导致相同的内存泄漏:
var aThing = null;
var outer = function() {
console.log('running');
var something = aThing;
var closure1 = function() {
if (something) {
console.log('something');
}
}
aThing = {
str: new Array(1000000).join('8')
};
function someMethod() {};
};
setInterval(outer, 1000);
以下是显示GC正在清理的等效时间线。
据我所知,在第一个版本中存在内存泄漏,因为变量'something'没有得到清理。为什么在第二个例子中是GC,而不是第一个?
答案 0 :(得分:2)
主要答案是,在您的第二个代码块中,closure1
的返回都没有(someMethod
或outer
),outer
之外的任何内容都没有他们),所以没有任何东西可以指代创建它们的上下文,并且可以清理上下文。但是,在第二个代码块中,someMethod
在返回时仍然存在,作为您分配给aThing
的对象的一部分,因此上下文不能用于GC。
让我们按照您的第一个块进行操作:
首次执行outer
后,我们(忽略了一堆细节):
+-------------+ aThing----->| (object #1) | +-------------+ | str: ... | +--------------------+ | someMethod |---->| (context #1) | +-------------+ +--------------------+ | something: null | | closure1: function | +--------------------+第二次执行后
:
+-------------+ aThing----->| (object #2) | +-------------+ | str: ... | +--------------------+ | someMethod |---->| (context #2) | +-------------+ +--------------------+ +-------------+ | something |---->| (object #1) | | closure1: function | +-------------+ +--------------------+ | str: ... | +--------------------+ | someMethod |---->| (context #1) | +-------------+ +--------------------+ | something: null | | closure1: function | +--------------------+第三次执行后
:
+-------------+ aThing----->| (object #3) | +-------------+ | str: ... | +--------------------+ | someMethod |---->| (context #3) | +-------------+ +--------------------+ +-------------+ | something |---->| (object #2) | | closure1: function | +-------------+ +--------------------+ | str: ... | +--------------------+ | someMethod |---->| (context #2) | +-------------+ +--------------------+ +-------------+ | something |---->| (object #1) | | closure1: function | +-------------+ +--------------------+ | str: ... | +--------------------+ | someMethod |---->| (context #1) | +-------------+ +--------------------+ | something: null | | closure1: function | +--------------------+
你可以看到它的发展方向。
由于第二个块永远不会保留对closure1
或someMethod
的引用,因此它们都不会将上下文保留在内存中。
我有点惊讶V8(Chrome的JavaScript引擎)没有优化此泄漏,因为只保留someMethod
,someMethod
实际上不使用something
或closure1
(或eval
或new Function
或debugger
),因此虽然理论上它通过上下文引用了它们,但静态分析会显示它们实际上不是使用,所以可以删除。但是闭包优化很容易打扰,我猜这里的东西令人不安。