In Kyle Simpson's new title, You don't know JS: ES6 and beyond, I find the following snippet:
WARNING Assigning an object or array as a constant means that value will not be able to be garbage collected until that constant’s lexical scope goes away, as the reference to the value can never be unset. That may be desirable, but be careful if it’s not your intent!
(Excerpt From: Simpson, Kyle. “You Don’t Know JS: ES6 & Beyond.” O'Reilly Media, Inc., 2015-06-02. iBooks.
This material may be protected by copyright.)
As far as I can see, he doesn't expand on this, and 10 minutes on Google turns up nothing. Is this true, and if so, what does "the reference to the value can never be unset" mean exactly? I have got into the habit of declaring variables that won't be changed as const, is this a bad habit in real concrete performance/memory terms?
WARNING Assigning an object or array as a constant means that value
will not be able to be garbage collected until that constant’s lexical
scope goes away, as the reference to the value can never be unset.
That may be desirable, but be careful if it’s not your intent!
That note sounds a bit more of a warning than is necessary (perhaps even a bit silly) and tries to make some sort of special case out of this situation.
With a const variable declaration, you can't assign to the variable something little like "" or null to clear its contents. That's really the only difference in regard to memory management. Automatic garbage collection is not affected at all by whether it is declared const or not.
So, if you would like to be able to change the contents of the variable in the future for any reason (including to manually remove a reference to something to allow something to be garbage collected sooner), then don't use const. This is the same as any other reason for using or not using const. If you want to be able to change what the variable contains at any time in the future (for any reason), then don't use const. This should be completely obvious to anyone who understand what const is for.
Calling out garbage collection as a special case for when not to use const just seems silly to me. If you want to be able to clear the contents of a variable, then that means you want to modify the variable so duh, don't use const. Yes, manually enabling garbage collection on a large data structure that might be caught in a lasting scope/closure is one reason that you might want to change the variable in the future. But, it's just one of millions of reasons. So, I repeat one more time. If you ever want to change the contents of the variable for any reason in the future, then don't declare it as const.
The garbage collector itself doesn't treat a const variable or the contents it points to any different than a var or let variable. When it goes out of scope and is no longer reachable, its contents will be eligible for garbage collection.
const has a number of advantages. It allows the developer to state some intent that the contents this variable points to are not to be changed by code and may allow the runtime to make some optimizations because it knows the contents of the variable cannot be changed. And, it prevents rogue or accidental code from ever changing the contents of that variable. These are all good things when used in an appropriate case. In general, you SHOULD use const as much as practical.
I should add the even some const data can still be reduced in size and make the majority of its contents available for garbage collection. For example, if you had a really large 100,000 element array of objects (that you perhaps received from some external http call) in a const array:
const bigData = [really large number of objects from some API call];
You can still massively reduce the size of that data by simply clearing the array which potentially makes the large number of objects that was in the array eligible for garbage collection if nothing else had a reference to them:
bigData.length = 0;
Remember, that const prevents assignment to that variable name, but does not prevent mutating the contents that the variable points to.
You could do the same thing with other built-in collection types such as map.clear() or set.clear() or even any custom object/class that has methods for reducing its memory usage.
That note in my book was referring to cases like this, where you'd like to be able to manually make a value GC'able earlier than the end of life of its parent scope:
var cool = (function(){
var someCoolNumbers = [2,4,6,8,....1E7]; // a big array
function printCoolNumber(idx) {
console.log( someCoolNumbers[idx] );
}
function allDone() {
someCoolNumbers = null;
}
return {
printCoolNumber: printCoolNumber,
allDone: allDone
};
})();
cool.printCoolNumber( 10 ); // 22
cool.allDone();
The purpose of the allDone() function in this silly example is to point out that there are times when you can decide you are done with a large data structure (array, object), even though the surrounding scope/behavior may live on (via closure) indefinitely in the app. To allow the GC to pick up that array and reclaim its memory, you unset the reference with someCoolNumbers = null.
If you had declared const someCoolNumbers = [...]; then you would be unable to do so, so that memory would remain used until the parent scope (via the closure that the methods on cool have) goes away when cool is unset or itself GCd.
Update
To make absolutely clear, because there's a lot of confusion/argument in some comment threads here, this is my point:
const absolutely, positively, undeniably has an effect on GC -- specifically, the ability of a value to be GCd manually at an earlier time. If the value is referenced via a const declaration, you cannot unset that reference, which means you cannot get the value GCd earlier. The value will only be able to be GCd when the scope is torn down.
If you'd like to be able to manually make a value eligible for GC earlier, while the parent scope is still surviving, you'll have to be able to unset your reference to that value, and you cannot do that if you used a const.
Some seem to have believed that my claim was const prevents any GC ever. That was never my claim. Only that it prevented earlier manual GC.
No, there are no performance implications. This note refers to the practise of helping the garbage collector (which is rarely enough needed) by "unsetting" the variable:
{
let x = makeHeavyObject();
window.onclick = function() {
// this *might* close over `x` even when it doesn't need it
};
x = null; // so we better clear it
}
This is obviously not possibly to do if you had declared x as a const.
The lifetime of the variable (when it goes out of scope) is not affected by this. But if the garbage collector screws up, a constant will always hold the value it was initialised with, and prevent that from being garbage-collected as well, while a normal variable might no more hold it.
The way garbage collectors (GC) work is when something is referenced by nothing ("cannot be reached"), the GC can safely say that something isn't used anymore and reclaim the memory used by that something.
Being able to replace the value of a variable allows one to remove a reference to the value. However, unlike var, const cannot be reassigned a value. Thus, one can't remove that constant from referencing the value.
A constant, like a variable, can be reclaimed when the constant goes "out of scope", like when a function exits, and nothing inside it forms a closure.
Related
var SomeObj = function() {
this.i = 0;
};
setTimeout(function() {
new SomeObj; // I mean this object
}, 0);
At what point is the SomeObj object garbage collected?
It is eligible for garbage collection as soon as it is no longer used.
That means immediately after the constructor call in your case.
How timely this actually happens is an implementation detail. If you run into GC issues, you need to dig into your specific Javascript engine.
An object that is not referenced from anywhere doesn't "exist" at all from the view of your program. How long it still resides somewhere in memory depends on the garbage collection characteristics of your interpreter, and when/whether it feels the need to collect it.
In your specific case, the object does become eligible for garbage collection right after it has been created and the reference that the expression yields is not used (e.g. in an assignment). In fact, the object might not get created at all in the first place, an optimising compiler could easily remove the whole function altogether - it has no side effects and no return value.
On a blog I see the following:
for (var key in map) {
if (map.hasOwnProperty(key)) {
var value = map[key];
// right, now we can get some work done
}
}
"Now you see that var key at the top of the for loop? That’s not
declaring a variable, oh no. It’s saying that somewhere else there’s a
variable called key"
Surely this is declaring a variable (if one named key did not previously exist in the scope chain)? What might the author mean by this?
Link: http://dannorth.net/2011/12/19/the-rise-and-rise-of-javascript/
Surely this is declaring a variable (if one named key did not previously exist in the scope chain)?
Yes, it is, within the function that for loop is in. (The variable is not limited to the for loop like it would be in, say, Java. Its scope is the entire function it's in.)
More about var (on my blog): Poor, misunderstood var
What might the author mean by this?
It sounds sarcastic, actually, like the author is trying to make a point by saying the opposite of what they mean.
Edit Since you've posted the link, here's the complete quote:
Now you see that var key at the top of the for loop? That’s not declaring a variable, oh no. It’s saying that somewhere else there’s a variable called key (right at the top of the nearest containing function, it turns out). Oh, and that key variable is visible all through that function, and any functions it contains, not just tucked away in the for loop. Brilliant!
The point he's trying to make there is that the variable isn't just limited to the for loop. But that initial statement is flatly incorrect. I know what he means, but it's not what he said.
Re the first point above, in ES6 JavaScript will be getting a new keyword, let, which would declare something only for the for loop:
// New in ES6, you probably can't use this yet
for (let key in map) {
// ^^^---------------- change is here
if (map.hasOwnProperty(key)) {
let value = map[key];
// ^^^----------------- probably want to change this one, too
// right, now we can get some work done
}
}
Some engines already support it, but you can't use it broadly yet, as many don't. And in theory the ES6 draft spec could change, though I really doubt it will. :-)
That, actually is a full definition of a variable from an iterator value that's fetched at the current iteration of variable map. It's a declaration + initialization.
To understand the notion of iterators you would have to go deeper and look into the underlying interpreter code which supports the value of the key to every iteration of the loop.
Also this: "Now you see that var key at the top of the for loop? That’s not declaring a variable, oh no. It’s saying that somewhere else there’s a variable called key" sounds stupid, doesn't explain anything and creates confusion.
I wouldn't read such articles, because such a statement shows that the author isn't really acquainted with the real world behind JavaScript - that is C/C++ or even assembly, which work at the basic memory level, and use constructs called iterators to support values to loops in case of data structures that are more advanced than simple arrays.
I'm just curious. Maybe someone knows what JavaScript engines can optimize in 2013 and what they can't? Any assumptions for nearest future? I was looking for some good articles, but still there is no "bible" in the internet.
Ok, let's focus on single quesiton:
Suppose I have a function which is called every 10ms or in a tight loop:
function bottleneck () {
var str = 'Some string',
arr = [1,2,3,4],
job = function () {
// do something;
};
// Do something;
// console.log(Date.getTime());
}
I do not need to calculate the initial values for variables every time, as you see. But, if I move them to upper scope, I will loose on variable lookup. So is there a way to tell Javasript engine to do such an obvious thing - precalculate variables' initial values?
I've careated a jsperf to clear my question. I'm experimenting with different types. I'm especially interested in functions and primitives.
if you need to call a function every 10ms, and it's a bottleneck, the first thought you should have is "I shouldn't call this function every 10ms". Something went wrong in the architecting you did. That said, see 1b in http://jsperf.com/variables-caching/2, which is about four times faster than your "cached" version - the main reason being that for every variable in your code, you're either moving up scope, or redeclaring. In 1b, we go up scope once, to get "initials", then set up local aliasses for its content, from local reference. Much time is saved.
(Concerns V8)
Well the array data itself is not created but an unique array object needs to be created every-time. The backing array for the values 1,2,3,4 is shared by these objects.
The string is interned and it is actually fastest to copy paste same string everywhere as a literal rather than referencing some common variable. But for maintenance you don't really want to do that.
Don't create any new function inside a hot function, if your job function references any variables from the bottleneck function then first of all those variables will become context allocated and slow to access anywhere even in the outer function and it will prevent inlining of the bottleneck function as of now. Inlining is a big deal optimization you don't want to miss when otherwise possible.
I never really gave much thought to garbage collection and I don't know whether or not it is necessary to take into account when making small javascript games/applications. Any advice is appreciated, but I will ask my specific question at the end.
A lot of the time I write code of this form:
var foos=new Array();
generateFoos();
function generateFoos()
{
foos=[];
for (fooIndex=0;fooIndex<numberOfFoos;fooIndex++)
{
foos[fooIndex]=new foo(Math.random(),Math.random());
}
}
function foo(bar,bas)
{
this.bar=bar;
this.bas=bas;
}
So my question is, when I say foos=[] (line 5), does this delete the objects in that array from the memory or do they float around somewhere, making the program larger and slower? What should I do if I want to call generateFoos() a loooot of times, like every time the user presses a key.
Thanks!
For a specific answer, since the accepted one doesn't actually answer the question directly, is that yes, foo = [] does de-reference any previous values in the array.
As Ales says, "An object becomes eligible for garbage collection when it becomes unreachable." Indeed, this is when the browser will clear such things from memory.
An important point, delete DOES NOT GARBAGE COLLECT.
You see this over and over, and even in the comments on this question. The delete keyword removes a property from an object and has nothing to do with garbage collection.
I also wanted to offer some advice on your code itself.
1) Use literals, not new, for basic data types
2) Don't call functions before you declare them. Yes, it works but its messy and harder to read later. Remember, you spend much more time reading your code than writing it. Make it easy to follow later.
3) Remember your function scope. Any variable declared without var goes global. With var, it remains within the scope of the function that contains it. Another way variables are scoped within a function is when they are passed in as named parameters.
4) Use var on your functions when creating them. In your code, your functions are globals.
5) Use spacing. Density of text is not next to godliness. You might be 20-something now with great eyesight, but you'll appreciate white space in just a very few short years.
6) Declare counters in for loops with var. Unless you want them to be global. And you almost never will.
Lets re-work your code now:
var numberOfFoos = 10,
foos = [];
var generateFoos = function(){
foos = [];
for( var fooIndex = 0; fooIndex < numberOfFoos; fooIndex++ ){
foos[ fooIndex ] = new foo( Math.random(), Math.random() );
}
},
foo = function( bar, bas ){
this.bar = bar;
this.bas = bas;
}
generateFoos();
console.log( foos );
To answer your question : An object becomes eligible for garbage collection when it becomes unreachable. If your program is not holding any other references to the objects in that array, they will be garbage collected.
The timing of the actual garbage collection depends on many aspects and on the choosen Garbage Collection algorithm but you should not worry about it when writing your code.
The best advice when considering Garbage collection is to leave the Garbage collector to do its job. Do not try to instruct it or help it (by e.g. nulling references manually). Focus on the problem and functional aspect of the code.
Do I have to destroy instances myself? ...if I don't assign them a variable...will they automatically go away?
new ImageUploadView();
vs
var Iu = ImageUploadView();
If there is no reference to an object in javascript, the garbage collector will clean it up.
The way the garbage collector works is it looks for javascript objects for which nobody has a reference to. If nobody has a reference to it, then it can't be used again, so it can be deleted and the memory it occupied reclaimed. On the other hand, if any javascript entity still has a reference to the object, then it is still "in use" and cannot be deleted.
In your first code example:
new ImageUploadView();
unless the constructor of the object stores away the this pointer into some other variable or object or creates some closure that causes references to the object to be held, then there will be no reference to this new object and it will be cleaned up by the garbage collector.
If you second code example:
var Iu = ImageUploadView();
as long as the Iu variable exists and stays in scope it will contain whatever the ImageUploadView() function returns. Note, the second example, is just executing a function and storing it's value. It is not necessarily creating anything. If ImageUploadView() just returns true, then that's all the Iu variable will contain.
The first method is fine. Assuming that the instance of ImageUploadView is appropriately cleaning up after itself, it will be collected by the garbage collector.
With large objects, it's not necessarily a good practice to assume that the browsers built in garbage collector will clean up once it's out of scope. You're better off to clear it ourself using "delete". For example:
delete MyImageUploadView;
Edit: it may be preferable to set the object to null if it isnt being referenced as a property.