Sunday, January 16, 2011

Memory Leaks in JavaScript and Solutions - Tutorial 1

Memory leaks in java script
----------------------------
In the first of these tutorials, let us highlight 3 major areas : Circular References , Anonymous Inner Functions, Closure.

 Problem 1 : Circular references.

So, we have an object, and we have a property on that object. Now we assign that property back to the object itself. Bam - memory leak.
A more realistic example is perhaps where we have object 1, then we have a property in that object which points to object 2. Now, add a property to object 2, which points back to object 1::



for (var i = 0; i < 10000; i  ) {
    var myDiv = document.createElement('div');
    var myDiv2 = document.createElement('div');


    myDiv.expandoProperty = myDiv2;
    myDiv2.expandoProperty = myDiv;
}



Solution
-----------

Nullify your properties.
When you are done with your crazy web of circular property references, simply nullify the properties:
 myDiv.expandoProperty = null;
 myDiv2.expandoProperty = null;

Read More on the next two problems



 Problem 2: Anonymous inner functions.

Well - it's not so much the inner function that's the problem, but the memory you allocate in the external function....
So say you have a function which has a load of memory it allocates for arrays and things.
Now, when we exit the scope of that function, we'd expect the memory to be marked for garbage collection.

But now, let's say we have an anonymous inner function in that function.
The function doesn't have to do anything, but the very fact that it has access to the local variables
in it's enclosing function means that those variables cannot be cleaned up.

function SetupLeak()
{
     var myDiv = document.getElementById('LeakedDiv');
     var myVar = null;
    // allocate loads of memory
    myVar = new Array(1000).join(new Array(2000).join("AAAA"));
    // inner function means our memory will not be able to be cleaned up
    myDiv.onclick = function() { };
}

Solution


Declare large variables outside the scope of the function.
By declaring our large myVar array variable external to the SetupLeak function, we can later on nullify it when we are done.
This will cause the memory to be cleaned up the next time the GC makes its rounds.

Some more inputs on this :

When making this, I found it useful to have a function which would cause the garbage collector in IE to fire,
and go and cleanup stuff it didn't need allocated anymore.
Interestingly, allocating 32k of memory seems to do the trick:

function FireCleanup() {
    var myArray = new Array;
    for (var i = 0; i < 8192; i  ) {
        myArray[i] = i;
    }
}


Problem 3: Closure
------------------------


A closure is the local variables for a function - kept alive after the function has returned, or
A closure is a stack-frame which is not deallocated when the function returns.
(as if a 'stack-frame' were malloc'ed instead of being on the stack!)

The following code returns a reference to a function:

function sayHello(name) {
  var text = 'Hello ' + name; // local variable
  var sayAlert = function() { alert(text); }
  return sayAlert;
}

A C programmer would think of the function as returning a pointer to a function,
and that the variables sayAlert a pointer to a function.There is a critical difference between a C pointer to a function,and a JavaScript reference to a function. In JavaScript, you can think of a function reference variable as having both a pointer to a function as well as a hidden pointer to a closure.
The above code has a closure because the anonymous function function() { alert(text); }
is declared inside another function, sayHello() in this example.
In JavaScript, if you use the function keyword inside another function, you are creating a closure.
In C, and most other common languages after a function returns, all the local variables are no longer
accessible because the stack-frame is destroyed.In JavaScript, if you declare a function within another function, then the local variables can remain accessible after returning from the function you called.

Another Example of Closure causing memory leak if used like this:

function localDocElem() {


var elem = document.getElementById(‘ClickElem’);


elem.bind(‘click’, function() { elem.innerHTML = ‘<span class=”highlight”>’ + elem.innerHTML + ‘</span’; });
}

This function simply attach a click event to a DOM element so whenever it is clicked the content gets “highlighted”.
However, the function attached to the DOM object “elem” also uses the DOM object, hence,
a circular reference is created between the DOM object and function object (in Javascript, even a function is maintained as an object)
 so the browser won’t garbage collect the local variable elem even after the localDocElem function returns.
 In this particular example, since a DOM object is connected with Javascript function,
 it is possible that the browser does not garbage collect either even the user leaves the page
 and the only way to claim the memory back is to shutdown the browser completely.


Solution
------------
Consider using global variable to hold large amount of data can reduce some of the risk above.
Let’s you have a Javascript that uses Ajax to to pull data from the server constantly,
you can use JQuery’s $.getJson method to archive this.
The returned data is better to be held in a global variable than local.

Hope you like this tutorial .

To read on Memory leak patterns in Java , click here :

http://codingbasics.blogspot.com/2011/01/java-best-practices-for-avoiding-memory.html

No comments:

Post a Comment

subversion video