I've logged the following Chrome bug, which has led to many serious and non-obvious memory leaks in my code:
(These results use Chrome Dev Tools' memory profiler, which runs the GC, and then takes a heap snapshot of everything not garbaged collected.)
In the code below, the someClass
instance is garbage collected (good):
var someClass = function() {};
function f() {
var some = new someClass();
return function() {};
}
window.f_ = f();
But it won't be garbage collected in this case (bad):
var someClass = function() {};
function f() {
var some = new someClass();
function unreachable() { some; }
return function() {};
}
window.f_ = f();
And the corresponding screenshot:
https://i.stack.imgur.com/ZSVf0.png
It seems that a closure (in this case, function() {}
) keeps all objects "alive" if the object is referenced by any other closure in the same context, whether or not if that closure itself is even reachable.
My question is about garbage collection of closure in other browsers (IE 9+ and Firefox). I am quite familiar with webkit's tools, such as the JavaScript heap profiler, but I know little of other browsers' tools, so I haven't been able to test this.
In which of these three cases will IE9+ and Firefox garbage collect the someClass
instance?
unreachable
function is never executed so nothing is actually logged.
As far as I can tell, this is not a bug but the expected behavior.
From Mozilla's Memory management page: "As of 2012, all modern browsers ship a mark-and-sweep garbage-collector." "Limitation: objects need to be made explicitly unreachable".
In your examples where it fails some
is still reachable in the closure. I tried two ways to make it unreachable and both work. Either you set some=null
when you don't need it anymore, or you set window.f_ = null;
and it will be gone.
Update
I have tried it in Chrome 30, FF25, Opera 12 and IE10 on Windows.
The standard doesn't say anything about garbage collection, but gives some clues of what should happen.
Section 13 Function definition, step 4: "Let closure be the result of creating a new Function object as specified in 13.2"
Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)
Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment. An outer Lexical Environment may, of course, have its own outer Lexical Environment. A Lexical Environment may serve as the outer environment for multiple inner Lexical Environments. For example, if a Function Declaration contains two nested Function Declarations then the Lexical Environments of each of the nested functions will have as their outer Lexical Environment the Lexical Environment of the current execution of the surrounding function."
So, a function will have access to the environment of the parent.
So, some
should be available in the closure of the returning function.
Then why isn't it always available?
It seems that Chrome and FF is smart enough to eliminate the variable in some cases, but in both Opera and IE the some
variable is available in the closure (NB: to view this set a breakpoint on return null
and check the debugger).
The GC could be improved to detect if some
is used or not in the functions, but it will be complicated.
A bad example:
var someClass = function() {};
function f() {
var some = new someClass();
return function(code) {
console.log(eval(code));
};
}
window.f_ = f();
window.f_('some');
In example above the GC has no way of knowing if the variable is used or not (code tested and works in Chrome30, FF25, Opera 12 and IE10).
The memory is released if the reference to the object is broken by assigning another value to window.f_
.
In my opinion this isn't a bug.
I tested this in IE9+ and Firefox.
function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; } //removing this fixes a massive memory leak
return function() {}; //or removing this
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
Live site here.
I hoped to wind up with an array of 500 function() {}
's, using minimal memory.
Unfortunately, that was not the case. Each empty function holds on to an (forever unreachable, but not GC'ed) array of a million numbers.
Chrome eventually halts and dies, Firefox finishes the whole thing after using nearly 4GB of RAM, and IE grows asymptotically slower until it shows "Out of memory".
Removing either one of the commented lines fixes everything.
It seems that all three of these browsers (Chrome, Firefox, and IE) keep an environment record per context, not per closure. Boris hypothesizes the reason behind this decision is performance, and that seems likely, though I'm not sure how performant it can be called in light of the above experiment.
If a need a closure referencing some
(granted I didn't use it here, but imagine I did), if instead of
function g() { some; }
I use
var g = (function(some) { return function() { some; }; )(some);
it will fix the memory problems by moving the closure to a different context than my other function.
This will make my life much more tedious.
P.S. Out of curiousity, I tried this in Java (using its ability to define classes inside of functions). GC works as I had originally hoped for Javascript.
Heuristics vary, but a common way to implement this sort of thing is to create an environment record for each call to f()
in your case, and only store the locals of f
that are actually closed over (by some closure) in that environment record. Then any closure created in the call to f
keeps alive the environment record. I believe this is how Firefox implements closures, at least.
This has the benefits of fast access to closed-over variables and simplicity of implementation. It has the drawback of the observed effect, where a short-lived closure closing over some variable causes it to be kept alive by long-lived closures.
One could try creating multiple environment records for different closures, depending on what they actually close over, but that can get very complicated very quickly and can cause performance and memory problems of its own...
O(n^2)
or O(2^n)
as an explosion, but not a proportional increase.
Maintain State between function calls Let’s say you have function add() and you would like it to add all the values passed to it in several calls and return the sum.
like add(5); // returns 5
add(20); // returns 25 (5+20)
add(3); // returns 28 (25 + 3)
two way you can do this first is normal define a global variable Of course, you can use a global variable in order to hold the total. But keep in mind that this dude will eat you alive if you (ab)use globals.
now latest way using closure with out define global variable
(function(){ var addFn = function addFn(){ var total = 0; return function(val){ total += val; return total; } }; var add = addFn(); console.log(add(5)); console.log(add(20)); console.log(add(3)); }());
function Country(){ console.log("makesure country call"); return function State(){ var totalstate = 0; if(totalstate==0){ console.log("makesure statecall"); return function(val){ totalstate += val; console.log("hello:"+totalstate); return totalstate; } }else{ console.log("hey:"+totalstate); } }; }; var CA=Country(); var ST=CA(); ST(5); //we have add 5 state ST(6); //after few year we requare have add new 6 state so total now 11 ST(4); // 15 var CB=Country(); var STB=CB(); STB(5); //5 STB(8); //13 STB(3); //16 var CX=Country; var d=Country(); console.log(CX); //store as copy of country in CA console.log(d); //store as return in country function in d
(function(){ function addFn(){ var total = 0; if(total==0){ return function(val){ total += val; console.log("hello:"+total); return total+9; } }else{ console.log("hey:"+total); } }; var add = addFn(); console.log(add); var r= add(5); //5 console.log("r:"+r); //14 var r= add(20); //25 console.log("r:"+r); //34 var r= add(10); //35 console.log("r:"+r); //44 var addB = addFn(); var r= addB(6); //6 var r= addB(4); //10 var r= addB(19); //29 }());
Success story sharing
setTimeout()
callback runs, that function scope of thesetTimeout()
callback is done and that whole scope should be garbage collected, releasing its reference tosome
. There is no longer any code that can run that can reach the instance ofsome
in the closure. It should be garbage collected. The last example is even worse becauseunreachable()
is not even called and nobody has a reference to it. Its scope should be GCed also. These both seem like bugs. There is no language requirement in JS to "free" things in a function scope.f()
is called, there are no actual references tosome
any more. It is unreachable and should be GCed.eval
is really special case. For example,eval
cannot be aliased (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…), e.g.var eval2 = eval
. Ifeval
is used (and since it cannot be called by a different name, that is easy to do), then we must assume it can use anything in scope.