admin管理员组文章数量:1302329
I was reviewing the slides in this presentation:
and on one of the slides, this code is presented with the suggestion that it creates a memory leak:
var a = function () {
var smallStr = 'x',
largeStr = new Array(1000000).join('x');
return function (n) {
eval(''); //maintains reference to largeStr
return smallStr;
};
}();
Closures can be another source of memory leaks. Understand what references are retained in the closure.
And remember: eval is evil
Can someone explain the issue here?
I was reviewing the slides in this presentation: 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 a = function () {
var smallStr = 'x',
largeStr = new Array(1000000).join('x');
return function (n) {
eval(''); //maintains reference to largeStr
return smallStr;
};
}();
Closures can be another source of memory leaks. Understand what references are retained in the closure.
And remember: eval is evil
Can someone explain the issue here?
Share edited Aug 6, 2013 at 22:26 Mike Samuel 121k30 gold badges227 silver badges252 bronze badges asked Aug 6, 2013 at 22:09 MedicineManMedicineMan 15.3k33 gold badges104 silver badges154 bronze badges 5-
3
I don't believe that is a memory leak as defined by the same presentation (which said a leak is "when a program repeatedly fails to return memory that it has obtained for temporary use"), because it's not happening repeatedly. But
largeStr
will be tying up a chunk of memory untila
goes out of scope. Alsoeval()
isn't evil, it's just almost always the wrong tool for the job. – nnnnnn Commented Aug 6, 2013 at 22:12 -
@nnnnnn: Especially here, where
eval
seems to be used to prevent static code analysis which would allow the garbage collector to collectlargeStr
even when a reference to the returned function is alive. – Bergi Commented Aug 6, 2013 at 22:27 - @MedicineMan: Did you "understand what references are retained in the closure" or not? – Bergi Commented Aug 6, 2013 at 22:29
-
1
@Bergi - So in this case
eval()
is the right tool for the job, if the job is to deliberately tie-up memory. (As long as people understand that the lesson is "be careful with closures", not "eval()
always ties up memory".) – nnnnnn Commented Aug 6, 2013 at 22:32 -
@nnnnnn: Which is, I fear, what many people would do. It should better be replaced by something like
if (false) return largeStr;
– Bergi Commented Aug 6, 2013 at 22:36
3 Answers
Reset to default 7If instead of returning a function that does
eval('');
you returned one that passes its argument
eval(n);
then someone could call a('largeStr')
to get the array, so the JavaScript interpreter cannot garbage collect the array.
Interpreters could realize that
eval('');
is equivalent to
;
but most are not smart enough to do that, so as soon as they see eval
they stop allowing GC of closed-over variables as long as the closure is reachable.
The memory leak arises when eval
can't effectively access closed-over variables because of the nature of its input:
eval('x' + (n-1));
Since 'x' + (n-1)
can't produce a string of JS that references largeStr
no input can lead to largeStr
being used but it is still pinned in memory.
To see the whole thing in action, play around with
var f = (function () {
var a = [,,,,,];
return function (x) { return eval(x); };
})();
alert(f('a.length'));
Okay let's consider what happens here;
var a = (function () { // `a` will be set to the return of this function
var smallStr = 'x',
largeStr = new Array(1000000).join('x');
return function (n) { // which is another function; creating a closure
eval('');
return smallStr;
};
}());
The inner function needs to be able to access all variables from the outer function, meaning as long as a reference to it exists, variables from the outer function can't be garbage collected and hence continue consuming memory after it has finished invoking and therefore may result in "memory leaks".
If you're dealing with large data like this and you're finished with it, set it to null
Let's rewrite the code just a little bit:
function makeClosure() {
var smallStr = 'x',
largeStr = new Array(1000000).join('x');
return function (n) {
eval(''); //maintains reference to largeStr
return smallStr;
};
}
var a = makeClosure();
assert(a() === 'x');
makeClosure
returns a function, hence a
is a function. However, that function still executes in the scope where it was defined (that's the definition of a closure). If you were to do:
function makeEnumerator() {
var count = 0;
return function () {
count++;
return count;
};
}
var enum = makeEnumerator();
assert(enum() === 1);
assert(enum() === 2);
enum
still has access to count
. Back to our case, the closure keeps a reference to smallStr
, which stays in memory. largeStr
is not retained and should be freed.
However, eval
also executes in the current scope and might use largeStr
. Because of that, the browser is forced to keep largeStr
as well.
Long story short, don't use eval :)
本文标签: javascriptHow do closures create memory leaksStack Overflow
版权声明:本文标题:javascript - How do closures create memory leaks? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741711787a2393886.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论