28

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.

Toran Billups
  • 27,111
  • 40
  • 155
  • 268

4 Answers4

13

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.

savetheclocktower
  • 1,443
  • 7
  • 12
  • This is a big step forward because most of the DOM elements that just hang around are the ones I was removing and never used. Thank you! – Toran Billups Apr 20 '09 at 15:58
7
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

MNasir
  • 137
  • 7
David Driver
  • 71
  • 1
  • 1
1

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.

Jon Lennart Aasenden
  • 3,920
  • 2
  • 29
  • 44
1

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.

Randolpho
  • 55,384
  • 17
  • 145
  • 179
  • 3
    You can't dispose of them in such a way that when the javascript garbage collector comes around, these elements are removed from memory? – Toran Billups Apr 20 '09 at 15:09
  • One would assume that to be the case, but apparently it's not. – Randolpho Apr 20 '09 at 15:48
  • 2
    Hmm... after some digging, you might try some of the solutions discussed on this page: http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/c76967f0-dcf8-47d0-8984-8fe1282a94f5 – Randolpho Apr 20 '09 at 15:51
  • 3
    You can definitely dispose of them, but it's hard, because there are all sorts of ways that elements can leak in IE that aren't obvious. The leak might originate in your code, or it might lie in jQuery itself. The latter is far less likely, but not impossible. – savetheclocktower Apr 20 '09 at 15:52