Read more

How to create memory leaks in jQuery

Henning Koch
February 27, 2015Software engineer at makandra GmbH

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

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

The only way that a jQuery 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.

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() Show archive.org snapshot :

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

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 the jQuery cache for 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() Show archive.org snapshot method explicitely traverses through all removed elements in order to clean up the jQuery Cache:

$('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:

$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 the jQuery cache blocks its memory from being reclaimed. You need to call $element.remove() (or remove a parent element), to remove the entry in the jQuery 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 the jQuery cache even exists. After all jQuery could simply attach event handlers to the Javascript object that represents the DOM element.

The reason for the existence of the jQuery cache 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:

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.

Posted by Henning Koch to makandra dev (2015-02-27 19:42)