I'm sure something similar has been asked before, but the sheer number of similar questions that don't answer my question force me to ask for myself.
Say you have the following code:
(function() {
"use strict";
var somearray=[1, 2, 3];
for(var i=0; i<somearray.length; i++) {
//do something
}
function loop() {
//do something repeatedly
requestAnimationFrame(loop);
}
loop();
})();
Since the variable i is defined in the same scope as a perpetual loop, does it continue to exist even if unused, waiting for the possibility that it may be used eventually? Or is JavaScript's (particularly Chrome's V8) garbage collector smart enough to determine whether it will be used or not?
I know that I can add console.log(i) to the loop somewhere and then i will continue to exist so long as it's referenced, but will it continue to exist unreferenced in a scope that continues to exist?
This not really a question about the garbage collector rather it is question about whether the closure is optimized. The closure optimizer of the execution environment might determine that the variables are not referenced (closed over) by the nested functions and then elide them from the closure environment. If that is the case, then they are candidates for collection. If the closure optimizer isn't run, or it determines i and somearray are referenced, or it cannot determine the lifetime of i and somearray, then they will not be collected as they are required by the closure environment.
A function has an implicit environment which includes all local variables in the enclosing function (and all functions enclosing it). This environment will capture any objects referenced by those variables. A closure optimizer statically analyzes the function to determine if the environment can be trimmed to elide the unreferenced variables. If the variables are elided from the environment they are candidates for collection as soon as the function invocation is concluded.
All modern JavaScript engines have a closure optimizer.
Some common things that might disable or limit the closure optimizer include being in a debug mode, using direct eval(), use of the arguments array, use of deprecated features such as caller.
As you can see, it clearly depends on what \\do something repeatedly does and in what context the code is executed in.
One way to ensure that they are not captured regardless of what \\do something repeatedly does is to ensure they are no longer in scope. An immediately executing function will do just that so changing your code to,
(function() {
"use strict";
(function() {
var somearray=[1, 2, 3];
for(var i=0; i<somearray.length; i++) {
//do something
}
})();
function loop() {
//do something repeatedly
requestAnimationFrame(loop);
}
loop();
})();
will ensure the array is not left hanging around in memory.
Related
In the book "You don't know JS: scopes & closures", Kyle simpson states that a block-scoped variable helps with garbage collection, here is the specific example:
function process(data) {
// do something interesting
}
{
let someReallyBigData = {};
process(someReallyBigData);
}
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
console.log("Clicked!");
}, false);
Now the above example is supposed to help with garbage-collection since the variable someReallyBigData will be dropped from memory as soon as the block ends, unlike this example, which doesn't help with garbage-collection:
function process(data) {
// do something interesting
}
var someReallyBigData = {};
process(someReallyBigData);
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
console.log("Clicked!");
}, false);
Now I am sure this guy is correct about the examples he provided (the first one); however, i am wondering whether or not everything would be the same if we used an anonymous IIFE (Immediately Invoked Function Expression) along with a normal var instead of the {} curly braces and the let variable. Let me turn that into an example:
function process(data) {
// do something interesting
}
(function(){
var someReallyBigData = {};
process(someReallyBigData);
}());
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
console.log("Clicked!");
}, false);
Looking at it from the surface, they both should do the same thing; since just as the block-scoped someReallyBigData variable could not be accessed anymore by anything after the block of code had executed, the code inside the anonymous function cannot be accessed by anything once it has executed, by anything, from anywhere.
So, do they really have the same effect on the garbage-collection mechanisms of the Javascript engine ? I was almost certain this was the case until I googled "anonymous function garbage-collection" and almost all the material that showed up said only negative things like "anonymous functions cause memory leaks" and etc.
I would be glad if someone could shed some light on this thing.
Please don't forget that my question is a bit specific to the examples I provided, thanks!
(V8 developer here.) Yes, there are several ways to make objects unreachable, including at least all of the following:
Put stuff in let-declared variables in a block scope
Put stuff into an IIFE
Clear the variable (var or let) when you're done with it: someReallyBigData = null;
The end result will be the same in all cases: objects that are no longer reachable are eligible for garbage collection.
Other notes based on the discussion here:
The advice quoted in the question makes sense for top-level code. Within a reasonably sized function, I wouldn't worry about it -- the function will probably return soon enough that there is no difference, so you don't need to burden yourself with such considerations.
There's a big difference between "an object can be freed now" and "an object will be freed now". Letting something go out of scope does not cause it to be freed immediately, and does not cause the garbage collector to run more often. It just means that whenever the garbage collector next decides to go looking for garbage, the object in question will be eligible.
IIFEs are IIFEs regardless of whether they're anonymous or not. Example:
(function I_have_a_name() {
var someReallyBigData = ...;
})();
// someReallyBigData can be collected now.
I_have_a_name(); // ReferenceError: I_have_a_name is not defined
Creating closures in and of itself does not keep things alive. However, if closures reference variables in their outer scope, then (of course!) those can't be collected as long as the closure is around. Example:
var closure = (function() {
var big_data_1 = ...;
var big_data_2 = ...;
return function() { return big_data_1.foo; }
})();
// big_data_2 can be collected at this point.
closure(); // This needs big_data_1.
// big_data_1 still cannot be collected, closure might need it again.
closure = null;
// big_data_1 can be collected now.
The optimizing compiler has little influence on all this. It usually operates on a per-function bases, and usually the top-level is not optimized (because most logic tends to be in functions). Within a function, the optimizing compiler is very well aware of the lifetimes of objects (that's part of what it means to be an optimizing compiler).
JavaScript only has block level scope when you use the let or const keywords in declarations. Just because you have {} alone does not create block-level scope (as is the case in most other languages).
Aside from that, garbage collection is implementation dependent and you would most likely not notice any difference in performance due to block scoping.
Anonymous functions can have an impact on garbage collection because the function can be set up in such a way that it doesn't have to be stored for potential calling later. A good example of this would be a function that needs to run only once (i.e. when the document is fully parsed):
window.addEventListener("DOMContentLoaded", function(){ . . . });
However, that doesn't mean that all anonymous functions provide this benefit because the function could wind up being stored (i.e. if it is returned from a function and then captured in a variable) or if the anonymous function sets up closures, then all bets are off.
Also, be aware that you can't unit test anonymous functions as simply as you can named functions.
I am wondering whether or not everything would be the same if we used an anonymous IIFE
Sure that's possible, it's what transpilers do to emulate block scopes as well. However IIFEs look a bit awkward, block scopes with let/const variables are easier to use. See also Will const and let make the IIFE pattern unnecessary?.
Now the first example is supposed to help with garbage-collection since the variable someReallyBigData will be dropped from memory as soon as the block ends, unlike the second example, which doesn't help with garbage-collection.
Notice that the word is helps, not enables. Today's engines can garbage-collect the variable just fine, as their optimiser sees that it's not used in the preserved closure. The block scope only makes this kind of static analysis easier.
I'd like to understand under which circumstances variables which are no further used are stored in closures and lead to memory leaks. My most preferred outcome would be "there are none", but this doesn't seem to be the case.
From what I understand, once a function is declared inside another function, its internal [[scope]] is assigned the LexicalEnvironment of its encapsulating function. This LexicalEnvironment has reference local variables and the entire scope chain at that point. This basically includes all free variables the function could access (from what I understood of lostechies, javascript closures explained).
Here the first issue arises: this should mean all those variables can be reached as long as the function lives. E.g. the following should already leak:
function a() {
let big = new Array(1000000).join('*'); //never accessed
//function unused() { big; }
return () => void 0;
}
let fstore = [];
function doesThisLeak() {
for(let i = 0; i < 100; i++) fstore.push(a());
}
doesThisLeak();
This luckily doesn't seem to be the case on my firefox. I've received several explanations to why this doesn't leak, from "the jitter is smart" to "LexicalEnvironment is a record type which means GC can collect the unused variables". I still don't know whether either is correct, whether this doesn't leak on all modern runtimes and why.
After further research, I found auth0, four types of leaks in javascript (sadly, there appears to be no html id to jump to, the relevant part is "4: Closures") which shows a way to trick whatever smart thing is collecting the unused variables. In above snippet, when just uncommenting the "unused" function, I do not see RAM usage ever going down again (it was already noted that it could be GC simply did not run for other reasons. However, so far, I am assuming it leaks. I also got told this was limited to firefox, but it appeared to produce similar behavior in chrome)
This example (in case it really does what i believe it does), shows that completely unused variables can leak due to a function declaration in the same scope.
To conclude my problems:
What is the reason for, in the above snippet, "big" getting collected (when "unused" is commented) and does this happen on all modern runtimes?
Assuming the example with the "unused" function not commented leaks, what are best practices to avoid such accidental leaks? Are there any more? I already got the suggestion of null'ing all local variables which are not further used at the end of functions, however, this seems absurdly ugly. I fear using temporary variables for pre-calculations and accidentally leaking.
PS: It is quite hard to make certain that this question has not already been asked in the jungle of questions about memory leaks with closures.
The compiler can examine the code of the returned function to see which free variables it references, and only those variables need to be saved in the closure, not the entire LexicalEnvironment. You can see this by examining the closure in the Javascript debugger.
function a() {
let big = new Array(1000000).join('*');
let small = "abc"; // is accessed
return (x) => small + x;
}
fun = a();
console.dir(fun);
function b() {
let big = "pretend this is a really long string";
function unused() { big; }
return () => void 0;
}
fun = b();
console.dir(fun);
When you expand the first function in the debugger, you'll see small in the Closure property, but not big. Unfortunately, the Chrome compiler doesn't seem to be clever enough to detect when the variable is referenced in an unused function that isn't returned, so it doesn't need to be saved, so we get a leak in b().
Any data that isn't saved in the closure becomes garbage and can be collected, so it won't leak.
After you call a() your fstore has reference to created function () => void only. After a() returns, its scope vars will be deleted. It means that no vars has reference to your new Array(1000000).join('*') and it will be garbage collected. It will be collected the same way if you uncomment unused function because it will deleted too. There are no leaks in your code.
pic1
You can think about Javascript scope chain with the problem.Identify a Function will create a new scope chain.If function's local scope do not have a local variable, it will reference to outside scope of lexical environment, it will keep over in the memory
here's a link
I'm interested in whether V8 does garbage collection on the contents of individual variables within a scope or whether it only does garbage collection on the entire scope?
So, if I have this code:
function run() {
"use strict";
var someBigVar = whatever;
var cnt = 0;
var interval = setInterval(function() {
++cnt;
// do some recurring action
// interval just keeps going
// no reference to someBigVar in here
}, 1000);
someBigVar = somethingElse;
}
run();
Will V8 garbage collect someBigVar? The closure in run() remains alive because of the setInterval() callback and obviously the cnt variable is still being used so the whole scope of run() cannot be garbage collected. But, there is no actual ongoing reference to someBigVar.
Does V8 only garbage collect an entire scope at a time? So, the scope of run() can't be garbage collected until the interval is stopped? Or is it smart enough to garbage collect someBigVar because it can see that there is no code in the interval callback that actually references someBigVar?
FYI, here's an interesting overview article on V8 garbage collection (it does not address this specific question).
Yes, it does. Only variables that are actually used inside of a closure are retained. Otherwise closure had to capture EVERYTHING that is defined in the outer scope, which could be a lot.
The only exception is if you use eval inside of a closure. Since there is no way to statically determine, what is referenced by the argument of eval, engines have to retain everything.
Here is a simple experiment to demonstrate this behaviour using weak module (run with --expose-gc flag):
var weak = require('weak');
var obj = { val: 42 };
var ref = weak(obj, function() {
console.log('gc');
});
setInterval(function() {
// obj.val;
gc();
}, 100)
If there is no reference to ref inside of the closure you will see gc printed.
I was reading the comments on an answer and saw this comment:
[the closure] doesn't persist the state of foo so much as creates a special scope containing (1) the returned function and (2) all the external variables referenced at the time of the return. This special scope is called a closure.
OK, so far so good. Now here is the interesting part that I didn't know about:
Case in point... if you had another var defined in foo that was not referenced in the return function, it would not exist in the closure scope.
I guess that makes sense, but what implications does this have other than memory use / perfomance?
Question -- If all variables in scope were included in the closure, what would that allow me to do that I cannot do with the current model?
I think you're taking that comment too literally. The comment is just saying that you can't access it outside the function scope (it's not publicly accessible), not that its not available at all within the function. The returned function will have access to all of the outer functions scope no matter what. You just can't access that scope outside the outer function if the inner function doesn't provide a way of accessing it.
For instance, this expression evaluates to 4:
function testClosure(){
var x = 2;
return function(y){
alert(eval(y));
}
}
var closure = testClosure();
closure("x+2"); //4
http://jsfiddle.net/dmRcH/
So x is available despite not being directly referenced
Further research
It appears that chrome and firefox at least do attempt to optimize this in the sense that if you're not providing ANY way to reference the x variable, it doesn't show up as being available in the debugger. Running this with a breakpoint inside a closure shows x as unavailable on Chrome 26 and Firefox 18.
http://jsfiddle.net/FgekX/1/
But thats just a memory management detail, not a relevant property of the language. If there is any possible way that you could reference the variable, it is passed, and my suspicion is that other browsers may not optimize this in the same way. Its always better to code to the spec than to an implementation. In this case though the rule really is: "if there's any possible way for you to access it, it will be available". And also, don't use eval because it really will keep your code from optimizing anything.
if you had another var defined in foo that was not referenced in the return function, it would not exist in the closure scope.
This is not entirely accurate; the variable is part of the closure scope, even though it may not be directly referenced inside the function itself (by looking at the function code). The difference is how the engines optimize unused variables.
For instance, unused variables in the closure scope are known to cause memory leaks (on certain engines) when you're working with DOM elements. Take this classical example for instance:
function addHandler() {
var el = document.getElementById('el');
el.onclick = function() {
this.style.backgroundColor = 'red';
}
}
Source
In above code, memory is leaked (in both IE and Mozilla at least) because el is part of the closure scope for the click handler function, even though it's not referenced; this causes a cyclic reference which can't be removed because their garbage collection is based on reference counting.
Chrome, on the other hand, uses a different garbage collector:
In V8, the object heap is segmented into two parts: new space where objects are created, and old space to which objects surviving a garbage collection cycle are promoted. If an object is moved in a garbage collection cycle, V8 updates all pointers to the object.
This is also referred to as a generational or ephemeral garbage collector. Albeit more complicated, this type of garbage collector can more accurately establish whether a variable is used or not.
JavaScript has no inherent sense of privacy so we use function scope (closure) to simulate this functionality.
The SO answer you reference is an example of the Module pattern which Addy Osmani does a great job explaining the importance of in Learning JavaScript Design Patterns:
The Module pattern encapsulates "privacy", state and organization
using closures. It provides a way of wrapping a mix of public and
private methods and variables, protecting pieces from leaking into the
global scope and accidentally colliding with another developer's
interface. With this pattern, only a public API is returned, keeping
everything else within the closure private.
Please have a look below code:
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Here what will be output? 0,1,2,3,4 not that will be 5,5,5,5,5 because of closure
So how it will solve? Answer is below:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
Let me simple explain, when a function created nothing happen until it called so for loop in 1st code called 5 times but not called immediately so when it called i.e after 1 second and also this is asynchronous so before this for loop finished and store value 5 in var i and finally execute setTimeout function five time and print 5,5,5,5,5
Here how it solve using IIFE i.e Immediate Invoking Function Expression
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
For more, please understand execution context to understand closure.
- There is one more solution to solve this using let (ES6 feature) but under the hood above function is worked
for(let i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Output: 0,1,2,3,4
I was curious about how the node.js pattern of nested functions works with the garbage collector of v8.
here's a simple example
readfile("blah", function(str) {
var val = getvaluefromstr(str);
function restofprogram(val2) { ... } (val)
})
if restofprogram is long-running, doesn't that mean that str will never get garbage collected? My understanding is that with node you end up with nested functions a lot. Does this get garbage collected if restofprogram was declared outside, so str could not be in scope? Is this a recommended practice?
EDIT I didn't intend to make the problem complicated. That was just carelessness, so I've modified it.
Simple answer: if value of the str is not referenced from anywhere else (and str itself is not referenced from restofprogram) it will become unreachable as soon as the function (str) { ... } returns.
Details: V8 compiler distinguishes real local variables from so called context variables captured by a closure, shadowed by a with-statement or an eval invocation.
Local variables live on the stack and disappear as soon as function execution completes.
Context variables live in a heap allocated context structure. They disappear when the context structure dies. Important thing to note here is that context variables from the same scope live in the same structure. Let me illustrate it with an example code:
function outer () {
var x; // real local variable
var y; // context variable, referenced by inner1
var z; // context variable, referenced by inner2
function inner1 () {
// references context
use(y);
}
function inner2 () {
// references context
use(z);
}
function inner3 () { /* I am empty but I still capture context implicitly */ }
return [inner1, inner2, inner3];
}
In this example variable x will disappear as soon as outer returns but variables y and z will disappear only when both inner1, inner2 and inner3 die. This happens because y and z are allocated in the same context structure and all three closures implicitly reference this context structure (even inner3 which does not use it explicitly).
Situation gets even more complicated when you start using with-statement, try/catch-statement which on V8 contains an implicit with-statement inside catch clause or global eval.
function complication () {
var x; // context variable
function inner () { /* I am empty but I still capture context implicitly */ }
try { } catch (e) { /* contains implicit with-statement */ }
return inner;
}
In this example x will disappear only when inner dies. Because:
try/catch-contains implicit with-statement in catch clause
V8 assumes that any with-statement shadows all the locals
This forces x to become a context variable and inner captures the context so x exists until inner dies.
In general if you want to be sure that given variable does not retain some object for longer than really needed you can easily destroy this link by assigning null to that variable.
Actually your example is somewhat tricky. Was it on purpose? You seem to be masking the outer val variable with an inner lexically scoped restofprogram()'s val argument, instead of actually using it. But anyway, you're asking about str so let me ignore the trickiness of val in your example just for the sake of simplicity.
My guess would be that the str variable won't get collected before the restofprogram() function finishes, even if it doesn't use it. If the restofprogram() doesn't use str and it doesn't use eval() and new Function() then it could be safely collected but I doubt it would. This would be a tricky optimization for V8 probably not worth the trouble. If there was no eval and new Function() in the language then it would be much easier.
Now, it doesn't have to mean that it would never get collected because any event handler in a single-threaded event loop should finish almost instantly. Otherwise your whole process would be blocked and you'd have bigger problems than one useless variable in memory.
Now I wonder if you didn't mean something else than what you actually wrote in your example. The whole program in Node is just like in the browser – it just registers event callbacks that are fired asynchronously later after the main program body has already finished. Also none of the handlers are blocking so no function is actually taking any noticeable time to finish. I'm not sure if I understood what you actually meant in your question but I hope that what I've written will be helpful to understand how it all works.
Update:
After reading more info in the comments on how your program looks like I can say more.
If your program is something like:
readfile("blah", function (str) {
var val = getvaluefromstr(str);
// do something with val
Server.start(function (request) {
// do something
});
});
Then you can also write it like this:
readfile("blah", function (str) {
var val = getvaluefromstr(str);
// do something with val
Server.start(serverCallback);
});
function serverCallback(request) {
// do something
});
It will make the str go out of scope after Server.start() is called and will eventually get collected. Also, it will make your indentation more manageable which is not to be underestimated for more complex programs.
As for the val you might make it a global variable in this case which would greatly simplify your code. Of course you don't have to, you can wrestle with closures, but in this case making val global or making it live in an outer scope common for both the readfile callback and for the serverCallback function seems like the most straightforward solution.
Remember that everywhere when you can use an anonymous function you can also use a named function, and with those you can choose in which scope do you want them to live.
My guess is that str will NOT be garbage collected because it can be used by restofprogram().
Yes, and str should get GCed if restofprogram was declared outside, except, if you do something like this:
function restofprogram(val) { ... }
readfile("blah", function(str) {
var val = getvaluefromstr(str);
restofprogram(val, str);
});
Or if getvaluefromstr is declared as something like this:
function getvaluefromstr(str) {
return {
orig: str,
some_funky_stuff: 23
};
}
Follow-up-question: Does v8 do just plain'ol GC or does it do a combination of GC and ref. counting (like python?)