What are some of the standard issues or coding patterns in jQuery which lead to memory leaks?
I have seen a number of questions related to the ajax() call or jsonp or DOM removal on StackOverflow. Most of the jQuery memory leak questions are focussed on specific issues or browsers and it would be nice to have a listing of the standard memory leak patterns in jQuery.
Here are some related questions on SO:
Resources on the web:
From what I understand, memory management in javascript is accomplished by reference counting - while a reference to an object still exists, it will not be deallocated. This means that creating a memory leak in a single page application is trivial, and can trip up those of use coming from a java background. This is not specific to JQuery. Take the following code for example:
function MyObject = function(){
var _this = this;
this.count = 0;
this.getAndIncrement = function(){
_this.count++;
return _this.count;
}
}
for(var i = 0; i < 10000; i++){
var obj = new MyObject();
obj.getAndIncrement();
}
It will look normal until you look at memory usage. Instances of MyObject are never deallocated while the page is active, due to the "_this" pointer (increase the max value of i to see it more dramatically.). (In older versions of IE they were never deallocated until the program exits.) Since javascript objects may be shared between frames (I don't recommend trying this as it is seriously temperamental.), there are cases where even in a modern browser javascript objects can hang around a lot longer than they are meant to.
In the context of jquery, references are often stored to save the overhead of dom searching - for example:
function run(){
var domObjects = $(".myClass");
domObjects.click(function(){
domObjects.addClass(".myOtherClass");
});
}
This code will hold on to domObject (and all its contents) forever, because of the reference to it in the callback function.
If the writers of jquery have missed instances like this internally, then the library itself will leak, but more often it is the client code.
The second example can be fixed by explicitly clearing the pointer when it is no longer required:
function run(){
var domObjects = $(".myClass");
domObjects.click(function(){
if(domObjects){
domObjects.addClass(".myOtherClass");
domObjects = null;
}
});
}
or doing the lookup again:
function run(){
$(".myClass").click(function(){
$(".myClass").addClass(".myOtherClass");
});
}
A good rule of thumb is to be careful where you define your callback functions, and avoid too much nesting where possible.
Edit: As was pointed out in the comments by Erik, you could also use the this pointer to avoid the unnescessary dom lookup:
function run(){
$(".myClass").click(function(){
$(this).addClass(".myOtherClass");
});
}