views:

45

answers:

3

I'm trying to display dynamically changeable data manipulating with DOM elements (adding/removing them). I found out a very strange behavior of almost all browsers: after I removed a DOM element and then add a new one the browser is not freeing the memory taken by the removed DOM item. See the code below to understand what I mean. After we run this page it'll eat step-by-step up to 150 MB of memory. Can anyone explain me this strange behavior? Or maybe I'm doing something wrong?

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
  <script type="text/javascript">
     function redrawThings() {
         // Removing all the children from the container
         var cont = document.getElementById("container");
         while ( cont.childNodes.length >= 1 ) {
            cont.removeChild(cont.firstChild);
         }

         // adding 1000 new children to the container
         for (var i = 0; i < 1000; i++) {
             var newDiv = document.createElement('div');
             newDiv.innerHTML = "Preved medved "  + i;
             cont.appendChild(newDiv);             
         }         
     }  
  </script>
  <style type="text/css">
    #container {
        border: 1px solid blue;
    }
  </style>
</head>
<body onload='setInterval("redrawThings()", 200);'>
  <div id="container"> </div>
</body>
</html>
+1  A: 

I can't reproduce this on FF 3.6.8/Linux, but 200 ms for a timer is rather small with that much of DOM re-rendering. What I notice on my machine is that when doing JavaScript-intensive things besides running this script, like typing in this answer box, memory usage increases, but is released again when I stop typing (in my case, to something around 16% of memory usage).

I guess that in your case the browser's garbage collector just doesn't have enough ‘free time’ to actually remove those nodes from memory.

Marcel Korpel
Igor Tkachenko
A: 

It's because removing a Node from the DOM Tree doesn't delete it from memory, it's still accessible, so the following code will work:

var removed = element.removeChild(element.firstChild);
document.body.appendChild(removed);

That code will remove the first child from element, and then after it has been removed, append it to the end of the document. There really is nothing you can do except make your code more efficient with less removals.

For more info, check out the Node.removeChild page at the Mozilla Developer Center.

Andrew Dunn
But if not assigned to anything (like in the OP) then the garbage collector would delete it from memory. This is not the reason.
slebetman
No, it is the reason, even if unassigned the garbage collector won't remove it. Even `removed = null` won't remove the reference from memory, the only way to do so is to refresh the page.
Andrew Dunn
Yes, I can confirm: garbage collector exists and it collects removed from DOM items. But it works on different browsers differently, e.g. Chrome's one started working in my case only after it reaches about 150MB.
Igor Tkachenko
A: 

Not sure if it'll affect timing, and it's probably really bad practice, but instead of looping through the child nodes, could you not just set the innerHTML of the div to "" ??

Alex