JavaScript封闭词汇环境中的内存泄漏 [英] JavaScript Memory leak from closure lexical environment
问题描述
我试图理解为什么下面的代码导致内存泄漏
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);
a href =https://i.stack.imgur.com/UNgC0.png =nofollow noreferrer>
但是这个代码非常微小,并不会导致内存泄漏:
var aThing = null; var outer = function(){console.log var something = aThing; var closure1 = function(){if(something){console.log('something'); }} aThing = {str:new Array(1000000).join('8')};函数someMethod(){};}; setInterval(outer,1000);
以下是等效的时间表,表明GC正在清理好。
我知道在第一个版本中有一个内存泄漏,因为变量'东西'没有得到清理。为什么它在第二个例子中被GC'ed,而不是第一个?
主要答案是,在你的第二个代码块,( closure1
或 someMethod
)都不存在 outer code>(
outer 外部没有任何内容指向它们),所以没有任何指向创建它们的上下文的东西,并且可以清除该上下文。作为您分配给
>,所以上下文不能被GC'd。 aThing
someMethod
让我们来看看你的第一个块发生了什么:
在第一次执行 outer
之后,我们已经(忽略了一大堆细节):
+ ------------- +
aThing -----> | (对象#1)|
+ ------------- +
| str:... | + -------------------- +
| someMethod | ----> | (上下文#1)|
+ ------------- + + -------------------- +
| something:null |
| closure1:函数|
+ -------------------- +
后 second 执行:
+ ------------- +
aThing -----> | (对象#2)|
+ ------------- +
| str:... | + -------------------- +
| someMethod | ----> | (上下文#2)|
+ ------------- + + -------------------- + + -------- ----- +
|东西| ----> | (对象#1)|
| closure1:函数| + ------------- +
+ -------------------- + | str:... | + -------------------- +
| someMethod | ----> | (上下文#1)|
+ ------------- + + -------------------- +
| something:null |
| closure1:函数|
+ -------------------- +
后 third execution:
+ ------------- +
aThing -----> | (对象#3)|
+ ------------- +
| str:... | + -------------------- +
| someMethod | ----> | (上下文#3)|
+ ------------- + + -------------------- + + -------- ----- +
|东西| ----> | (对象#2)|
| closure1:函数| + ------------- +
+ -------------------- + | str:... | + -------------------- +
| someMethod | ----> | (上下文#2)|
+ ------------- + + -------------------- + + -------- ----- +
|东西| ----> | (对象#1)|
| closure1:函数| + ------------- +
+ -------------------- + | str:... | + -------------------- +
| someMethod | ----> | (上下文#1)|
+ ------------- + + -------------------- +
| something:null |
| closure1:函数|
+ -------------------- +
您可以
由于第二个块永远不会保留对 closure1
或 someMethod
,它们都不会将上下文保留在内存中。
我对V8(Chrome的JavaScript引擎)未优化这泄漏了,因为只保留 someMethod
,并且 someMethod
实际上并不使用 something 或
closure1
(或 eval
或 new Function
或调试器
),所以虽然理论上它通过上下文引用它们,但静态分析表明它们不能实际使用,所以可能是下降。但封闭优化很容易打扰,我猜那里有一些令人不安的东西。
I am trying to understand why the following code causes a memory leak
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);
Here is the timeline showing memory increasing from Google Chrome:
but this code which is a very slight variation does not cause the same memory leak:
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);
Here is the equivalent timeline showing that GC is cleaning up OK.
I understand that in the first version there is a memory leak because the variable 'something' does not get cleaned up. Why is it being GC'ed in the second example but not the first?
The primary answer is that in your second code block, neither of the closures (closure1
or someMethod
) survives the return of outer
(nothing outside outer
refers to them), and so there's nothing left that refers to the context where they were created, and that context can be cleaned up. In your second code block, though, someMethod
survives the return, as part of the object that you're assigning to aThing
, and so the context cannot be GC'd.
Let's follow what happens with your first block:
After the first execution of outer
, we have (ignoring a bunch of details):
+-------------+ aThing----->| (object #1) | +-------------+ | str: ... | +--------------------+ | someMethod |---->| (context #1) | +-------------+ +--------------------+ | something: null | | closure1: function | +--------------------+
after the second execution:
+-------------+ aThing----->| (object #2) | +-------------+ | str: ... | +--------------------+ | someMethod |---->| (context #2) | +-------------+ +--------------------+ +-------------+ | something |---->| (object #1) | | closure1: function | +-------------+ +--------------------+ | str: ... | +--------------------+ | someMethod |---->| (context #1) | +-------------+ +--------------------+ | something: null | | closure1: function | +--------------------+
after the third execution:
+-------------+ 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 | +--------------------+
You can see where this is going.
Since the second block never retains a reference to closure1
or someMethod
, neither of them keeps the context in memory.
I'm slightly surprised that V8 (Chrome's JavaScript engine) doesn't optimize this leak away, since only someMethod
is retained, and someMethod
doesn't actually use something
or closure1
(or eval
or new Function
or debugger
), so although in theory it has references to them via the context, static analysis would show that they can't actually be used and so could be dropped. But closure optimization is really easy to disturb, I guess something in there is disturbing it.
这篇关于JavaScript封闭词汇环境中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!