18

i am using jquery and doing something like this

DOM:

<div id="parent"></div>

JS:

var _doSomeThing = function()
{
    //some codes
}

$(function()
{
    // appending div and binding methods to span
    $('#parent').append('<span>1</span>');
    $('#parent').append('<span>2</span>');
    $('#parent span').bind('click', _doSomeThing);
});

function _clearDiv()
{
    //clear div
    $('#parent').html('');
}

//sometime in future, call clear div
_clearDiv();

Now my question is, do binding events to DOM and later just removing the elements from DOM leads to memory leakage?

If yes, how to solve this problem?

sites
  • 21,417
  • 17
  • 87
  • 146
Praveen Prasad
  • 31,561
  • 18
  • 73
  • 106

5 Answers5

19

the jQuery html method attempts to prevent memory leaks by removing event handlers for any elements that are deleted as a result of calling .html('') on a jQuery object.

From the 1.4.2 source

html: function( value ) {
    if ( value === undefined ) {
        return this[0] && this[0].nodeType === 1 ?
        this[0].innerHTML.replace(rinlinejQuery, "") :
            null;
    } 
    // See if we can take a shortcut and just use innerHTML
    // THE RELEVANT PART
    else if ( typeof value === "string" && !rnocache.test( value ) &&
        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

        value = value.replace(rxhtmlTag, fcloseTag);

        try {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                // Remove element nodes and prevent memory leaks
               if ( this[i].nodeType === 1 ) {
                   jQuery.cleanData( this[i].getElementsByTagName("*") );
                   this[i].innerHTML = value;
                }
            }

        // If using innerHTML throws an exception, use the fallback method
        } 
        catch(e) {
            this.empty().append( value );
        }
    } 
    else if ( jQuery.isFunction( value ) ) {
        this.each(function(i){
            var self = jQuery(this), old = self.html();
            self.empty().append(function(){
                return value.call( this, i, old );
            });
        });

    }
    else {
        this.empty().append( value );
    }
    return this;
}

We can see that the jQuery.cleanData() function is called. Here is the source for that

cleanData: function( elems ) {
    var data, id, cache = jQuery.cache,
        special = jQuery.event.special,
        deleteExpando = jQuery.support.deleteExpando;

    for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
        id = elem[ jQuery.expando ];

        if ( id ) {
            data = cache[ id ];

            if ( data.events ) {
                for ( var type in data.events ) {
                    if ( special[ type ] ) {
                        jQuery.event.remove( elem, type );

                    } else {
                        removeEvent( elem, type, data.handle );
                    }
                }
            }

            if ( deleteExpando ) {
                delete elem[ jQuery.expando ];

            } else if ( elem.removeAttribute ) {
                elem.removeAttribute( jQuery.expando );
            }

            delete cache[ id ];
        }
    }
}

This looks in the jQuery.cache object for any event type properties on the events object property of the data object relating to each element that will be deleted when calling .html('') and removes them.

To basically explain how the standard event binding works, when a function is bound as a handler to an event raised on an element using jQuery, a data object is added as a property to the jQuery.cache object. This data object contains an events property object that will have a property created on it with a name matching the event type to which you wish to bind the event handler function. this property will contain an array of functions that should be called when the event is raised on the element, so the event handler function is added to this array. If this is the first event handler function for the event type and element in question, the jQuery.event.handle function with a call to apply (using the element as the context such that this in the function execution context will refer to the element) is registered with the browser using addEventListener/attachEvent.

When an event is raised, the jQuery.event.handle function will call all of the functions in the array on the property of the events property object of the data object matching the event type and the element on which the event was raised.

So in summary, html('') shouldn't cause memory leaks as a number of defensive measures are in place to prevent them.

Russ Cam
  • 124,184
  • 33
  • 204
  • 266
  • @russ i think you are right yesterday i did lot of testing and came to conclusion that .html(''); will also avoid memory leaks , while JS's innerHTML='', will cause memory leaks. i am wondering is jquery makes an internal list of elements on which we have called $()bind() function (ie bound some event), so that on window.unload we can remove those events or jquery does this automatically for us. do any have idea of this. – Praveen Prasad Feb 24 '10 at 07:35
  • jQuery automatically binds a function to remove event handlers on `window.onunload` as part of the intial script execution. You'll find the relevant code in the 1.4.2 source just before the block of Sizzle code - http://code.jquery.com/jquery-1.4.2.js :) – Russ Cam Feb 24 '10 at 17:46
  • you are correct, even jquery 1.3.2 version clear events bound to elements on window unload. so i think we dont have to write our codes to clear bound events to avoid memory leaks. thanx – Praveen Prasad Feb 25 '10 at 19:10
  • Doesn't everything get garbage collected on page refresh/reload ??? This should be normal behavior, regardless of jQuery...? That's why you have to be more careful about memory leaks in SPA applications... right?! – Legends Feb 06 '17 at 02:41
  • @Legends GC on page refresh/reload can be very much dependent on the browser and its implementation of the interaction between the DOM and JavaScript Engine, e.g. http://javascript.crockford.com/memory/leak.html – Russ Cam Feb 06 '17 at 03:05
2

Can't comment on the leakage problem but you could simply use .empty() instead of .html(''). That way you'd clean the innerHTML and remove any bound event handlers.

alex
  • 479,566
  • 201
  • 878
  • 984
aefxx
  • 24,835
  • 6
  • 45
  • 55
2

Yes, because jQuery maintains a list of the attached event handlers to make unhooking them easier and in order to explicitly unhook them for you when the page is unloaded (which works around a more serious memory leak in IE). (So does Prototype, can't speak for other libs.) The solution is to unhook them before removing the elements (either directly, or via empty).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

You can always use $('#parent span').unbind(); just to be sure

PatrikAkerstrand
  • 45,315
  • 11
  • 79
  • 94
0

Since you're constantly referring to $('#parent'), you should create a reference to that object in the global scope so that jQuery isn't constantly looking for the object on each request. Doing this, you're essentially caching the reference to the object, which will cut down on memory usage tremendously.

_parent = $('#parent');

...

function(){ _parent.append('<span>1</span>'); }

Edit: I picked up this tip from this article on jQuery Performance Rules

Adrian J. Moreno
  • 14,350
  • 1
  • 37
  • 44
  • 1
    Tremendously? Have you profiled memory usage before and after to verify this claim? – PatrikAkerstrand Feb 23 '10 at 21:47
  • I was having so many memory leaks on a jQuery application that MSIE would close itself after 10 minutes. After making this one adjustment, the leaks went away and memory stabilized at a reasonable amount. – Adrian J. Moreno Feb 23 '10 at 22:24
  • 3
    I wouldn't call it a memory "leak" not caching jQuery objects, but a practice that could potentially lead to bad performance, which is what is sounds like you were experiencing in IE, the browser family that has generally implemented less of the native methods (`Selectors API`, `document.getElementsByClassName`) than other browsers and therefore needs to rely on a JavaScript DOM traversal implementation to achieve the same result. I would try and avoid global namespace pollution too by scoping the variables that you cache jQuery objects in as locally as possible. – Russ Cam Feb 23 '10 at 22:44