How to dispose of DOM elements in JavaScript to avoid memory leaks

41,341

Solution 1

The tricky part is figuring out where a reference still exists to the offending nodes.

You're doing this the hard way — you're adding all the markup to the page, then removing the stuff you don't want. I'd do it this way instead:

var div = document.createElement('div');
// (Don't append it to the document.)

$(div).html(xhtml);

var stuffToKeep = $(div).find("form:eq(1)> *").filter(
  function() {
    return $(this).attr('id') !== '';
  }
);

parentObj.append(stuffToKeep);

// Then null out the original reference to the DIV to be safe.
div = null;

This isn't guaranteed to stop the leak, but it's a good start.

Solution 2

function discardElement(element) {
    var garbageBin = document.getElementById('IELeakGarbageBin');
    if (!garbageBin) {
        garbageBin = document.createElement('DIV');
        garbageBin.id = 'IELeakGarbageBin';
        garbageBin.style.display = 'none';
        document.body.appendChild(garbageBin);
    }
    // move the element to the garbage bin 
    garbageBin.appendChild(element);
    garbageBin.innerHTML = '';
}

Source

Solution 3

remove() and its ilk only remove elements from the DOM. They still exist in memory somewhere.

This is a known problem with AJAX and Web 2.0. There's little you can do aside from designing your site to ensuring that you occasionally have a page refresh to wipe things away.

Solution 4

I think the "always growing memory consumption" is somewhat misunderstood. It has to do with memory management inside the browser (and also on Windows in general) more than it has to do with your JS code. Memory managers typically keep allocating (or avoid releasing) until they have to. On systems based on pagefiles and memory mapping, allocating and releasing memory is very time consuming.

So even if your nodes are released from the actual document structure and DOM - there is hardly any way to measure it from Windows itself in "realtime".

Try this:

  1. Start Taskmanager, switch to process tab and watch memory of your browser

  2. Load in a HTML document that allocates 100.000 nodes, watch the memory consumption grow

  3. Click a button in the page that releases all the nodes. Notice that little happens in terms of memory release.

  4. Now minimize the browser-window, and maximize it again. In 90% of all cases the browser now dumps the memory if doesnt use and you will see how much it really consumes.

You can have a browser consume 500 megabytes of ram (not above mentioned example), but when you minimize and maximize it, it releases everything and suddenly it only uses 50 megabytes.

Share:
41,341
Toran Billups
Author by

Toran Billups

Swiss Army Knife: Elixir, React & Tailwind

Updated on June 21, 2020

Comments

  • Toran Billups
    Toran Billups almost 4 years

    I have an application that allows a user to view details on a specific case w/out a postback. Each time a user requests data from the server I pull down the following markup.

    <form name="frmAJAX" method="post" action="Default.aspx?id=123456" id="frmAJAX">
    <div>
    <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" />
    </div>
    <div>
    <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" />
    </div>
    <div id="inner">
    <!-- valid info here --!>
    </div>
    </form>
    

    Next I take the above and innerHTML it to a new DOM element like so:

       success: function(xhtml) {
            var tr = document.createElement('tr');
            var td = document.createElement('td');
            var container = document.createElement('div');
    
            obj.parentNode.parentNode.parentNode.insertBefore(tr, obj.parentNode.parentNode.nextSibling);
    
            td.appendChild(container);
            container.innerHTML = xhtml;
            tr.appendChild(td);
    

    but after the above, I use some jQuery to remove the nasty aspnet junk

    $('form:eq(1)').children().each(
        function() {
            if ($('form:eq(1)').find('div').filter(function() { return $(this).attr('id') == ''; }).remove());
        }
    );
    
    //Capture the remaining children
    var children = $('form:eq(1)').children();
    
    // Remove the form
    $('form:eq(1)').remove();
    
    // append the correct child element back to the DOM
    parentObj.append(children);
    

    My question is this - When using IESieve I notice no actual leaks but an ever growing number of DOM elements (thus memory usage).

    What can I improve on in the client-side to actually cleanup this mess? Note- both IE7/8 show these results.

    EDIT: I did finally get this working and decided to write a short blog post with complete source code.