DOM: why is this a memory leak? - javascript

Consider this quote from the Mozilla Docs on JavaScript memory leaks:
function addHandler() {
var el = document.getElementById('el');
el.onclick = function() {
this.style.backgroundColor = 'red';
}
}
The above code sets up the element to turn red when it is clicked. It
also creates a memory leak. Why? Because the reference to el is
inadvertently caught in the closure created for the anonymous inner
function. This creates a circular reference between a JavaScript
object (the function) and a native object (el).
Please explain the above reasons of leakage in a simple and concise way, I'm not getting the exact point.
Does the site/page face a security problem because of the leakage? How do I avoid them? What other code can cause memory leaks? How can I tell when a memory leak has occurred?
I'm an absolute beginner to the topic of memory leaks. Could someone clarify this stuff for me, step by step?Also can someone help me clarify this statement "This creates a circular reference between a JavaScript object (the function) and a native object (el)."

There are two concepts that will help you understand this example.
1) Closures
The definition of a closure is that Every inner function enjoys access to its parent's function variables and parameters.
When the addHandler() function finishes, the anonymous function still has access to the parent's variable el.
2) Functions = memory
Every time you define a function a new object is created.
What makes this example slightly confusing is that onclick is an event that can only be set to a DOM element once.
So surely el.onclick = function(){}; will just overwrite the old function right?
Wrong! every time addHandler runs, a new function object is created.
In conclusion:
Each time the function runs it will create a new object, with a closure containing el. Seeing as the anonymous function maintains access to el, the garbage collector cannot remove it from memory.
The anon function will maintain access to el, and el has access to the function, that is a circular reference, which causes a memory leak in IE.

Whenever you define a function in JavaScript an execution context is created for it; this execution context contains references to all the variables in the scope chain, starting from the global scope all the way up to the local scope:
function test()
{
var el = document.getElementById('el');
el.onclick = function() {
// execution context of this function: el, test
alert('hello world');
}
}
When test() is done, the anonymous function is not recycled yet because it's now assigned to an element of the DOM; i.e. it's being referenced by a property of the DOM element.
At the same time, the DOM element itself is also part of the function's execution context and now can't be recycled due to the cyclic reference, even though it's not immediately obvious that it's actually used; you can find a demonstration of that in this answer.
That said, nowadays, most JavaScript engines (even those found in IE) use a more advanced garbage collector that can identify unused variables a whole lot better, using techniques such as mark-and-sweep or generational / ephemeral garbage collection.
To make sure you don't run into problems on any browser (though, due to the typical lifespan of a page, this is mostly theoretical):
document.getElementById('el').onclick = function() {
alert('hello world');
}

Also see the more information section of the MS article on the issue:
This memory leak occurs because DOM objects are non-JScript objects.
DOM objects are not in the mark-and-sweep garbage collection scheme of
JScript. Therefore, the circular reference between the DOM objects and
the JScript handlers will not be broken until the browser completely
tears down the page.
but note that in contrast to what is stated in that article (memory will be reclaimed when the browser goes to a new page), this article confirms that a bug in IE 6 caused the memory to be leaked forever.

JavaScript's memory management usually works like this: "as long as it is possible to reach it, keep it". This is basically the paradigm that's behind any garbage collection driven memory model.
Garbage collectors tend to be very good at what they do, they even detect if a certain group of elements is only reachable within this very group of elements. Those groups are also called circular reference, since if you follow the references you'll end up at an element you already visited: you've run a circle.
However, in your example you actually have two objects from two different "worlds":
Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.
And this is exactly what happens and can cause memory leaks.

Related

Clean-up of elements that are no longer referenced, and were never added to the document

Suppose I create a new element:
let canvas = document.createElement('canvas');
Now, later on in the script, I remove any JS references to it.
canvas = null;
Does the canvas element itself still exist, taking memory? Or will it be garbage collected like any other unreferenced object? Note that I haven't actually added it to the document.
Does the <canvas> element itself still exist, taking memory? Or will it be garbage collected like any other unreferenced object?
Yes, it still exists for the time being. Yes, it will be garbage collected in due time.
Other posters seem to be slightly confused about the difference in GC behavior between the canvas variable and the <canvas> element. Variables are allocated on the stack, not the heap. They occupy some tiny amount of memory on the stack as long as they are in scope. They remain in scope by virtue of being in the call chain. The memory they use is released when the function is exited and stack frame popped.
Elements, like other objects, are allocated on the heap, and subject to garbage collection. They are GC'd when no longer referenced. The <canvas> element could be made to be no longer referenced either by setting any variable referring to it to null or something else, or the (only) variable referring to it going out of scope.
There is of course one other case relating to memory management of variables, which is closures. A variable which is closed over continues to occupy (a tiny amount of) memory as long as the enclosed function is "in scope", in other words, something references it. The value of such a variable--be it a DOM element or an JS object or anything else--will not and cannot be GC'd until the function in the closure goes out of scope. Little example:
function a() {
const div = document.createElement('div');
return function() {
console.log(div);
};
}
function b() {
const func = a();
}
When b is entered, storage is allocated on the stack for func. a is called, which creates the DOM element and returns the inner function. At this point, div remains allocated because it has been closed over and is referenced from within the inner function. The DOM element remains in the heap. Once b exits, the variable func is popped off the stack frame, meaning that nothing refers to the closure function any longer. That means that div is no longer in scope. That in turns means that the element is no longer referenced and will be GC'd (eventually).
Bottom line is that you don't need to worry about any of this. It just works, barring pathological cases or engine bugs.
You can utilize Take Heap Shot, Record Allocation Time, Record Allocation Profile at Profiles tab at DevTools to determine the memory status of a variable.
See
How to detect the memory allocations that are triggering garbage collection in JavaScript?
Memory Terminology
How to Record Heap Snapshots
Uncover DOM leaks
The heap profiler has the ability to reflect bidirectional
dependencies between browser native objects (DOM nodes, CSS rules) and
JavaScript objects. This helps to discover otherwise invisible leaks
happening due to forgotten detached DOM subtrees floating around.
DOM leaks can be bigger than you think. Consider the following sample - when is the #tree GC?
var select = document.querySelector;
var treeRef = select("#tree");
var leafRef = select("#leaf");
var body = select("body");
body.removeChild(treeRef);
//#tree can't be GC yet due to treeRef
treeRef = null;
//#tree can't be GC yet due to indirect
//reference from leafRef
leafRef = null;
//#NOW can be #tree GC
#leaf maintains a reference to it's parent (parentNode) and recursively up to #tree, so only when leafRef is nullified is the
WHOLE tree under #tree a candidate for GC.
Memory Management
If there are no other references to canvas, it should be garbage collected. You can be certain of this by checking heap snapshot.
First of all, there is a large caveat that not every implementation uses the same garbage collection algorithm because it hasn't been standardized. Namely, older versions of IE.
Quoting the MDN documentation:
Internet Explorer 6 and 7 are known to have reference-counting garbage
collectors for DOM objects
However, most modern browsers use mark-and-sweep garbage collection:
As of 2012, all modern browsers ship a mark-and-sweep
garbage-collector. All improvements made in the field of JavaScript
garbage collection (generational/incremental/concurrent/parallel
garbage collection) over the last few years are implementation
improvements of this algorithm, but not improvements over the garbage
collection algorithm itself nor its reduction of the definition of
when "an object is not needed anymore".
Mark-and-Sweep will remove an object when it is unreachable. So, in your case, if you assign a new value to the canvas variable, your newly created element will be unreachable and be marked for garbage collection. It may take up memory for a brief period of time, until the garbage collector runs. Also, because you used let it may be garbage collected as soon as the block scope it's located in is no longer needed in memory.
Okey, I can give you a worthy answer right now, because I've checked it out by myself. In your case, you have just made a new variable and set its value to document.createElement('canvas').
Until you decide to append that variable into your DOM, it's nothing more than just a value stored inside a variable. And with setting the value to null, the variable is now being stored and takes memory as a normal, single, empty variable.
The situation would be quite contrary, if you would append it into your document. The element wouldn't just be deleted with setting the variable's value to null and then it would waste memory.

javascript declaring variables or not

So this is puzzling to me. Are variables expensive?
Let's take a look at this code:
var div1 = document.createElement('div');
var div2 = document.createElement('div');
div2.appendChild(div1);
Now this one
var div2 = document.createElement('div');
div2.appendChild(document.createElement('div'));
So, on the second example, I'm doing the same thing as on the first one. Is it more expensive to declare a variable than not? Do I save memory by just creating and using an element on the fly?
EDIT: this is a sample code to illustrate the question. I know that in this specific case, the memory saved (or not) in minimal.
Variables are extremely cheap in JavaScript, yet, no matter how cheap they are, everything has a cost.
Having said that, it's not a noticable difference, and you should be thinking more about making your code readable rather than performing micro-optimizations.
You should be using variables to save repeated operations. In your example above, it's likely you need the variable pointing to the newly created div, or you'll be able to do nothing to it... unless you end up retrieving it from the DOM; which will prove many times more costly than the variable, as DOM manipulation is one of the slowest parts of JavaScript.
In theory, there is a tiny little amount of memory you save by not doing the variable declaration, because a variable declaration requires that the JavaScript engine creates a property on the variable binding object for the execution context in which you declare it. (See the specification, Section 10.5 and associated sections.)
In reality, you will never notice the difference, not even on a (relatively) slow engine like IE's. Feel free to use variables wherever they make the code clearer. Creating a property on a variable binding object (e.g., declaring a variable) is a very, very, very quick operation. (Avoid it at global scope, of course, but not because of memory use.)
Caveat: If the variable refers to a big memory structure that you only hold temporarily, and you create closures within that function context, the big memory structure may (in some engines) be kept in memory longer than necesary. Example:
function foo(element) {
var a = /* ...create REALLY BIG structure here */;
element.addEventListener("event", function() {
// Do something **not** referencing `a` and not doing an `eval`
}, false);
}
In an unsophisticated engine, since the event handler is a closure over the context of the call to foo, in theory a is available to the handler and so what it references remains "referencible" and not available for garbage collection until/unless that event handler function is no longer referencible. In any decent engine (like the V8 engine in Chrome), because you neither refer to a nor use eval within the closure (event handler), it's fine. If you want to defend yourself from mediocre engines, set a to undefined before foo returns, so the engine (no matter how mediocre) knows that the event handler won't be referencing that structure even if it were to reference a.
Yes, you save memory, no it is in no way an amount that matters.

Renaming the 'this' keyword

I'm concerned that I might be using a code pattern that leaks memory. Here's a pseudocode example:
window.user = new User();
user.getBasicInfo(function(basicInfo){
user.name = basicInfo.name;
user.getDetailedInfo(function(detailedInfo){
user.favoriteColor = detailedInfo.favoriteColor;
});
});
In other words, I'm not using the 'this' keyword to refer to the user object; I'm referring directly to the user object stored within the window object.
Obviously, the JavaScript 'this' keyword has given lots of people lots of trouble. I've seen some people rename 'this' to make it more clear as they descend a scope chain:
window.user = new User();
user.getBasicInfo(function(basicInfo){
var userInOuterScope = this;
userInOuterScope.name = basicInfo.name;
userInOuterScope.getDetailedInfo(function(detailedInfo){
var userInInnerScope = this;
userInInnerScope.favoriteColor = detailedInfo.favoriteColor;
});
});
Not as pretty, but it seems like the scope chain might be less convoluted in that case.
Can the first approach leak memory? Can the second? To avoid leaking memory, must I pass everything as a parameter (and never refer to objects outside of the current scope)?
The potential to "leak memory" is not something relevant to the question you seem to be asking. That is, neither of the approaches have implications on memory use, at least not in any way clear to me.
The reason you might prefer to make use of the this facility in your code is that you might want a population of objects. In your case, you're apparently using the object as a singleton, so it makes no difference. However, that's really a special case, and you'd quickly find that it doesn't work so well if you've got 100 "User" objects.
Preserving the value of this (and it's not really "renaming" this; it's copying its value into another variable) inside a closure could result in a memory leak, or rather, could be part of a larger memory-leaking setup, but it's not problematic in and of itself. Making copies of object references is something that happens all the time.
edit — the "leakage" problem with closures comes about when a combination of things happens:
Some objects are referenced by variables in the closure scope (not weird or harmful in and of itself);
Functions with references to the closure scope "escape" from a function call, either by being returned or by being exported via global state side-effects (like registering an event handler) (also not weird or harmful in and of itself);
The population of these exported functions grow, or the functions themselves allocate more room when they're invoked and preserve references to the space allocated in the closure, or the exported functions end up directly referenced by DOM nodes (this is particularly a problem in IE).
Really, JavaScript doesn't have any unique problems with memory leaks that any language with real closures has. For a vast amount of actual JavaScript software in this world (utility code wired into web pages), it's pretty rare that memory leakage is a problem, I suspect, though the IE issue with DOM references has probably crashed a few browsers over the years (which probably hardly surprised the hapless users).
I don't like pushing frameworks on people, but it's definitely true that framework authors must worry about this stuff. Trusting in your framework to keep the DOM clean by making sure you only attach event handlers and data to the DOM via framework facilities is therefore a Good Idea.
It doesn't appear that you have any circular references in your code, so I wouldn't worry about memory leaks. You're not setting objects equal to other objects as far as I see ( http://jsfiddle.net/Akkuma/UTL3B/ as a quick example how it could lead to memory leaks).
Here are a few references on memory leaks
http://www.ibm.com/developerworks/web/library/wa-memleak/
http://www.javascriptkit.com/javatutors/closuresleak/index.shtml
http://javascript.crockford.com/memory/leak.html
Additionally, you can use Chrome's Heap Snapshot tool to be able to tell whether or not you are having memory leaks worth worrying about.
JavaScript's this is not the same this like Java's or C#'s. It represents the current context of something being executed.

IE memory leak from referencing DOM elements in closures?

During an interview recently, I was asked, "What's dangerous about using closures when referencing DOM elements, like this code does?"
var firstNameValue = (function(elementId) {
var firstName = document.getElementById(elementId);
return firstName.value;
})("firstName");
Apparently, unbeknownst to me, the above code creates a memory leak in IE. The given rationale was pretty vague and I didn't understand it, but apparently this may only apply to older IE versions?
Anyone can elaborate on this?
The garbage collector used in the guts of IE to deal with memory allocated by/for the DOM has no idea how to free memory that may be dangling along allocated by the JScript engine. Thus, it just ignores such things.
So you bind an event handler to a DOM element (or something like that), and your event handler is a function created inside an invocation of some other function, and that other function has a local array with a billion things in it, well, those billion things live on and on long after the DOM element itself is junked, and even long after the page that contained it has been freed (I think; it's been a while).
function bindHandler(domElement) {
var hoHumWhatever = generateGiganticObjectNow();
domElement.onclick = function() {
alert("oww you clicked me");
};
}
Now that "hoHumWhatever" variable is maintained in the closure. When the page is reloaded or the DOM modified such that the element is thrown away, the DOM garbage collector will fail to do anything with the attribute that's pointing into JScript-owned memory. JScript, on the other hand, doesn't know that the DOM node has been freed, so it thinks the closure memory is still referenced.
I admit that this may be inaccurate in some particulars, but that's the basic problem. Various people have written about this, including Mr. Crockford and (I think) ppk at quirksmode.
edit — Upon more carefully rereading the code you posted, I think that might be an example of the similar but opposite case: the little function returns a reference to a part of a DOM value, so maybe somebody's saying that JScript will hang onto the DOM memory (instead of vice-versa). Now, in this particular case, I'm a little doubtful, because I don't see how anything "escapes" from that closure except the simple reference to the DOM attribute, which should be a primitive string instance and so which really shouldn't cause a problem. These matters can be deceptive however so I'll just sit here and scratch my head.
The following code doesn't create any closures (see below) or memory leaks.
var firstNameValue = (function(elementId) {
var firstName = document.getElementById(elementId);
return firstName.value;
})("firstName");
A bit of investigation with IE 6 shows that the code doesn't create a memory leak. I added 1,000 divs with a big slab of Lorem ipsum and unique ids, then ran 1000 anonymous functions per the above code and every time I refreshed the page it stubbornly returned to the memory use it had before I opened the page. Even adding several thousand more elements to the page to go over 100mb didn't faze it, IE kept returning to the original size.
So either this is a trick question (i.e. the correct answer is "There is no persistent closure and no circular reference so there is no memory leak") or whoever thought it up didn't write it correctly.
If it isn't a trick question, see if you can get whoever wrote it or asked the question to give you a demo of it in action.
Closure
The simple explanation of a Closure is
that ECMAScript allows inner
functions; function definitions and
function expressions that are inside
the function bodes of other functions.
And that those inner functions are
allowed access to all of the local
variables, parameters and declared
inner functions within their outer
function(s). A closure is formed when
one of those inner functions is made
accessible outside of the function in
which it was contained, so that it may
be executed after the outer function
has returned. At which point it still
has access to the local variables,
parameters and inner function
declarations of its outer function.
Those local variables, parameter and
function declarations (initially) have
the values that they had when the
outer function returned and may be
interacted with by the inner function.
Richard Cornford et al,"Javascript Closures", http://www.jibbering.com/faq/notes/closures/

Memory leak in JavaScript (Chrome)

I'm calling a function 50 times a second, which does some expensive things as it is painting alot on a <canvas> element.
It works great, no problems there, but I just took a look at the memory usage and it was stealing 1MB a second of my RAM. Chrome seems to garbage collect, as it went down each minute or so, but then the usage grew again.
What I tried is putting return at certain places in my function so as to decide what part of my function exactly causes the leak. I've been able to cut it down to a specific line of code, after which the evil part comes, but I don't really know how to solve it.
My questions are:
What tool is available to effectively measure JavaScript memory leaks in Chrome?
Would it be effective to set variables to null / undefined after they have been used, something like disposing them?
If the source code is really necessary I wouldn't hestitate to post it here, but I must admit that it's both long and perhaps a little ununderstandable for others.
I'm just going to pull this quote directly, linked from the article;
Speaking of memory leaks, breaking circular references — the cause of the leaks — is usually done with simple null assignment. There’s usually no need to use delete. Moreover, null‘ing allows to “dereference” variables — what delete would normally not be able to do.
var el = document.getElementById('foo');
// circular reference is formed
el.onclick = function() { /* ... */ };
// circular reference is broken
el = null;
// can't `delete el` in this case, as `el` has DontDelete
For these reasons, it’s best to stick with null‘ing when breaking circular references.
delete Explained
Look at heap profile under the Profiles tab in Chrome's developer tools for information about memory usage.
You can do the following to prevent memory leaks:
Test your code with JSLint, to see if that will give you some pointers.
Use the var keyword to give your variables function scope, so they can be garbage collected when they go out of scope. Without the var keyword variables have global scope.
Use delete variable; statements to remove the object as well as the reference from memory. Setting the variable to null will only remove the object from memory, but not its reference.

Categories