Posted over 2 years ago. Visible to the public. Repeats.

How to create memory leaks in jQuery

Note that the information in this card pertains to jQuery 1 and jQuery 2.0 and 2.1. Future versions of jQuery will probably attach information directly to DOM nodes.


jQuery doesn't store information about event listeners and data values with the element itself. This information is instead stored in the global $.cache object. Every time you add an event listener or data value to a jQuery object, $.cache gains another entry.

The only way that a $.cache entry gets deleted is when you call remove() on the element that put it there!

Since cache entries also have a pointer back to the element that spawned them, it is easy to create DOM elements that can never be garbage-collected.

Below are some ways to create memory leaks. jQuery 2.0 and 2.1 do more or less the same thing, though the store is no longer accessible as $.cache, but everything below will still work:

Mixing jQuery with native DOM manipulation is dangerous

When you remove element using the browser's native DOM manipulation methods, the jQuery cache is not cleaned up. This leads to DOM elements that are detached from the DOM, but can never be garbage-collected.

E. g. if you use removeChild():

Copy
var $element = $('<div></div>'); $element.on('click', function() { ... }); $element.appendTo(document.body); var nativeElement = $element.get(0); document.body.removeChild(element);

Now the element will remain detached from the DOM and stay in memory forever, even if the $element and nativeElement variables go out of scope.

The same applies if you remove elements with innerHTML=:

Copy
var $element = $('<div></div>'); $element.on('click', function() { ... }); $element.appendTo(document.body); document.body.innerHTML = '';

While setting an element's innerHTML property to a new string is a very performant operation, it fails to clean up $.cache entries of any elements that get removed. Again the element will remain detached from the DOM and stay in memory forever, even if the $element variables go out of scope.

Note that jQuery's html() method explicitely traverses through all removed elements in order to clean up $.cache.

Copy
$('body').html('');

Detached elements cannot be collected if they have data or event handlers

Let's say that you create a DOM element through jQuery, set a data attribute on it, but then don't attach it to the DOM:

Copy
$element = $('<div></div>'); $element.data('foo', 'bar');

After the last line in the code sample above, $element can no longer be garbage-collected, even if the $element variable goes out of scope. This is because the pointer in $.cache blocks its memory from being reclaimed. You need to call $element.remove() (or remove a parent element), to remove the entry in $.cache and allow $element to be garbage-collected.

Note that if you do attach $element to the DOM you can make the fair assumption that will either stay there forever, or that someone will probably remove() it in the future.

Why oh why doesn't jQuery store event handlers with the DOM element?

You might wonder why $.cache even exists. After all jQuery could simply attach event handlers to the Javascript object that represents the DOM element.

The reason for $.cache's existence is that old versions of Internet Explorer could not garbage-collect Javascript objects with circular references. For instance, the following object graph could not be reclaimed by old IE's garbage collector if jQuery would attach event handlers to the element:

Copy
var $element = $('<div>Close me</div>'); $element.on('click', function() { $element.remove(); });

Since $element would now contain a reference to the click handler and vice versa, both elements can no longer be garbage collected even if the variables go out of scope.

The Javascript engines of modern IE versions no longer have this issue. This is probably why jQuery 2.x broke browser compatibility and only supports IE9+.

Growing Rails Applications in Practice
Check out our new e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Author of this card:

Avatar
Henning Koch
Last edit:
about 2 years ago
by Henning Koch
Keywords:
1.8, 1.9, 1.10, 1.11, 1.12, 1.13, internet, explorer, jQuery.cache
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Henning Koch to makandropedia