Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript memory leaks

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?

like image 720
Praveen Prasad Avatar asked Feb 23 '10 08:02

Praveen Prasad


People also ask

Can JavaScript memory leak?

The JavaScript engine allocates memory when you create objects and variables in your application, and it is smart enough to clear out the memory when you no longer need the objects. Memory leaks are caused due to flaws in your logic, and they make way for poor performance in your application.

Is JavaScript memory safe?

Memory safe languages include Rust, Go, C#, Java, Swift, Python, and JavaScript. Languages that are not memory safe include C, C++, and assembly.

How do I stop JavaScript heap out of memory?

Open the Start menu, search for Advanced System Settings, and select the Best match. In the Variable name field enter NODE_OPTIONS. In the Variable value field enter --max-old-space-size=4096. This value will allocate 4GB of virtual memory to Node.


2 Answers

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.

like image 112
Russ Cam Avatar answered Oct 23 '22 22:10

Russ Cam


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).

like image 26
T.J. Crowder Avatar answered Oct 23 '22 20:10

T.J. Crowder