30

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)."

Community
  • 1
  • 1
Maizere Pathak.Nepal
  • 2,383
  • 3
  • 27
  • 41
  • 1
    http://www.javascriptkit.com/javatutors/closuresleak/, http://www.google.com/search?q=explanation+of+javascript+memory+leaks – CBroe Apr 02 '13 at 09:58
  • 1
    @GrantKiely its from MDN – Maizere Pathak.Nepal Apr 02 '13 at 10:00
  • http://javascript.crockford.com/memory/leak.html – Ram Apr 02 '13 at 10:02
  • @undefined hey i need a very simple explanation – Maizere Pathak.Nepal Apr 02 '13 at 10:03
  • Can you please let me know, how exactly you are implementing the above handler to call on the click ? – Abhishek Apr 02 '13 at 10:03
  • @Abhisheks.net this isn't my code and i dont know where to implement it .What is there ,it may be a modular programming – Maizere Pathak.Nepal Apr 02 '13 at 10:06
  • possible duplicate of [Javascript memory leaks after unloading a web page](http://stackoverflow.com/questions/1077840/javascript-memory-leaks-after-unloading-a-web-page) – Ram Apr 02 '13 at 10:09
  • @undefined no not at all.I never knew that,this is my own problem – Maizere Pathak.Nepal Apr 02 '13 at 10:14
  • 4
    @Maizere: the place where you got the quote (MDN, which I've now linked to in the body of the question) explains this particular memory leak very well. Is there a specific thing you are not understanding or are you looking to go in-depth into closures? – Crescent Fresh Apr 02 '13 at 10:20
  • 6
    By the way, I want to emphasize that this is a *Microsoft-only bug*. By putting such circular references in your code, you further degrade the IE experience and thereby encourage the user to switch to a better, safer browser, so do so at every opportunity. – Michael Lorton Apr 02 '13 at 10:26
  • 1
    @CrescentFresh I m not getting this statement "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)." Not so good at english so need a simple to the point explanation – Maizere Pathak.Nepal Apr 02 '13 at 10:29
  • This IE circular references between DOM and Jscript bug was apparently fixed - see http://support.microsoft.com/kb/929874. – FruitBreak May 08 '13 at 13:35
  • In above code are we really creating a closure, because the inner function is not using local variable "el" of outer function. Please correct me if i m wrong. – Ratnesh Lal Feb 09 '17 at 05:05

4 Answers4

20

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.

gkiely
  • 2,987
  • 1
  • 23
  • 37
  • 9
    *"In this case, `this` is referring to el."* ... that's true, but this particular fact does not have anything to do with closures. That's a property of event handlers, whether they are closures or not. The fact that `el` is accessible in the inner function is important. – Felix Kling Apr 02 '13 at 10:21
  • @FelixKling Thanks! I removed that part from my answer. – gkiely Apr 02 '13 at 10:32
  • 2
    just out of curiosity would the functions go out of scope if the element is removed from the DOM or would both still lurk around because of the circular references? – Orangepill May 15 '13 at 03:01
  • Your second point doesn' seem correct to me. In Firefox 65, the `onclick` method shorthand absolutely overrides the previous value, thus the previous subscription is inherently destroyed. – James Wright Feb 04 '19 at 20:40
9

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');
}
Community
  • 1
  • 1
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
2

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.

Old Pro
  • 24,624
  • 7
  • 58
  • 106
1

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":

Circular references

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.

Zeta
  • 103,620
  • 13
  • 194
  • 236