Being a C developer, I feel the urgent need to free memory as accurately as possible. But after reading some posts about memory management in JavaScript I still have some questions about that topic. As far as I understand, there is a garbage collector that will automatically free the memory when an object isn't referenced anymore and the GC can find the time. So an object that is held in a local variable is very likely to be freed when the variable goes out of scope.
Let's assume I have some custom elements (tab control, data table, inputs etc.). The classes of those custom elements have member variables where several data for the specific element is stored. Also event listeners can be attached to the elements the custom element consists of.
Let's say I have following html markup:
<my-tabbar>
<my-tab id="myTab">
<my-input></input>
<my-dataTable></my-dataTable>
</my-tab>
</my-tabbar>
Now I want to remove <my-tab>. Is it sufficient to just do
var element = document.getElementById ("myTab");
element.parentNode.removeChild (element);
element = null;
Or do I have to dereference every object of the child elements and remove their event listeners?
Setting an object to null makes it an empty object. Once the object is out of scope, the GC (when it runs) will clear it up. Setting local objects to null won't do you any good.
Example:
function whatever()
{
var something = null;
console.log( typeof( something ) );
}
whatever();
Output:
"object"
For the removal of a child node, please see this:
Does removeChild really delete the element?
Source(s):
https://www.w3schools.com/js/js_datatypes.asp
It is sufficient to remove the element. You don't even have to set the variable to null afterwards – you can trust the JS engine to free the memory when it goes out of scope.
Related
I did this simple test in Chromium Console:
var e = document.querySelector('#myElement');
e.remove();
//But here, 'e' still references the original node even though its no longer in the DOM.
I don't want this behavior. I would like 'e' to act more like a Weak Reference, whereby it would change to NULL or UNDEFINED or something indicating the node has been destroyed.
I understand that the problem is due to the fact that the node technically still exists even though its not part of the DOM anymore. I speculate it will hang around until the browser deems an appropriate time to garbage collect it.
So instead of simply doing .remove(), is there a way to really destroy/delete the node so that all variables referencing it will become undefined or null or some effect that can be detected later?
Thanks in advance!
Also, I speculate this behavior will vary highly between browsers. So any feedback that mentions Non-Chromium browsers is highly welcome also. :)
If a standalone variable holds a reference to an object, and that variable can still be referenced, the object will not get picked up by the garbage collector's mark-and-sweep algorithm, and the object will continue to exist at least as long as the variable can still be referenced.
Given a reference to an object, you cannot destroy it such that other references to the object break.
JavaScript doesn't provide the sort of manual memory control you're looking for, unfortunately.
You can put the element into a WeakSet, after which removing the element from the DOM will eventually cause the element being removed from the WeakSet if nothing else can possibly reference the element - but that's not really what you're looking for.
There do exist WeakRefs, an extremely new API, which allow variables to be specially declared such that what they reference can be garbage collected despite them still being referenceable:
const ref = (() => {
const e = document.querySelector('#myElement');
const ref = new WeakRef(e);
e.remove();
return ref;
})();
With the above, calling ref.deref() will eventually give you undefined if nothing else can reference the element and it has been garbage collected. (Until it gets garbage collected, .deref() will give you the element.)
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.
Sometimes I see in Javascript functions that, if the conversion of a variable to jQuery is used repeatedly, then it can be assigned to a local variable first:
$variable = $(variable);
Is this necessary, and how much is the cost of conversion?
No matter what, storing the object is faster than having to re-instantiate a jQuery object every time you want to use jQuery methods on it...even if it's miniscule for caching $(this) or $(anObject).
A term used to describe this method of "store now, use later" is "caching". The reason it's often called "caching" is because caching refers to storing a reference to something once and using that, without going back out to grab the same thing again, later (very non-technical, non-100% accurate description).
The major point is dealing with selectors. jQuery has to query the DOM every time, which is the expensive part. Generating the object and storing references isn't that expensive compared to DOM manipulation (and jQuery processing your selection in the first place).
If you're simply creating a jQuery object out of an object reference, it's not nearly as devastating, as the processing that takes place is the creation of the jQuery object...so it's really limited to whatever jQuery does for that. It's still good practice and still prevents some unnecessary processing. For example, this:
var element = document.getElementById("div_id");
$(element).someMethod();
// Later:
$(element).someOtherMethod();
is slightly inefficient, since a new jQuery object is created each time. It could easily be condensed to store a reference to a single jQuery object in a variable, and reference that.
The one caveat I can think of is that it isn't a live list of elements (if selecting DOM elements). For example, you may want to cache all elements with the class testing-class, like so:
var myelements = $(".testing-class");
But if another element is added to the DOM with the testing-class class, myelements will not be reflected. It will have the same, previous list. So in that case, the DOM will obviously need to be re-queried and update myelements.
To me, the best practice for caching is within a scope....not the entire page. If you are running a function, and it selects some elements, cache it at the beginning, and use that. But don't cache it globally and use it throughout your page; cache it for an execution cycle.
For example, I would do this:
function someFunc() {
var elements = $(".class-stuff");
// Use `elements` here
// code
// Use `elements` here
someOtherFunc(elements);
}
function someOtherFunc(el) {
// Use `el` here
}
someFunc();
// Some time later:
someFunc();
but I wouldn't do this:
var elements = $(".class-stuff");
function someFunc() {
// Use `elements`
}
function someOtherFunc() {
// Use `elements`
}
someFunc();
someOtherFunc();
// Some time later
someOtherFunc();
It depends on what the variable is. If the original variable is just a single DOM element then it's not particularly expensive - the DOM traversal has already been done so all you're doing is wrapping that element in the jQuery pseudo-array and attaching the prototype.
However if the original variable is a selector, then you absolutely should cache the result to avoid repeated conversions from DOM -> element list.
In any event, it's good practise not to repeat yourself, so caching $(variable) is just good code hygiene.
If the $(variable) is being called anyway this assignment has basically no cost -- this is only storing a reference to the object in memory.
Purists might point out that because the jQuery object is now stored it can't be garbage collected, and this is true. So I guess if you had lots of these it could cause a memory issue, but in itself it has no cost to speak of.
The reason it is done is because there is a cost associated with creating the object, that is the $(variable) part. That if done many times could be expensive. Store a reference to the object means only one needs to be created.
Another important point: The following statement
var $variable = $(variable);
could act different if it is done in a calling context of a closure. That is if there is a function defined in the scope of the var statement that variable will "stick around" for the function to use. This could have the same effects as described above (no gc and pointer memory) with the addition of a longer lifetime. (Because it will stay as long as the function has potential to be called.)
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.
Please excuse my question if it doesnt make much sense.
I am using javascript to create a dom element
to do that i create an object ( obj ={}) and fill out the properties as i go, one of which is the dom element to be created. once the element is created and appended to the document i do not need the object to occupy any space in the memory so i was thinking i should remove it.
how would i go about doing that?
thank you
The object's going to exist in memory as soon as it's in the DOM, and the obj property that holds it is really holding a reference to it, not a copy. So as far as I know, the memory required to keep the reference as a property of obj should be negligible. In which case I wouldn't worry about removing it at all.
here is how:
my_var = null;
//or remove it
delete my_var;
if you define you object as locale like
var obj ={}
it will be automatically undefined at the end of the function.