How to remove DOM elements without memory leaks?

48,258

Solution 1

The DOM preserves all DOM nodes, even if they have been removed from the DOM tree itself, the only way to remove these nodes is to do a page refresh (if you put the list into an iframe the refresh won't be as noticable)

Otherwise, you could wait for the problem to get bad enough that the browsers garbage collector is forced into action (talking hundreds of megabytes of unused nodes here)

Best practice is to reuse nodes.

EDIT: Try this:

var garbageBin;
window.onload = function ()
    {
    if (typeof(garbageBin) === 'undefined')
        {
        //Here we are creating a 'garbage bin' object to temporarily 
        //store elements that are to be discarded
        garbageBin = document.createElement('div');
        garbageBin.style.display = 'none'; //Make sure it is not displayed
        document.body.appendChild(garbageBin);
        }
    function discardElement(element)
        {
        //The way this works is due to the phenomenon whereby child nodes
        //of an object with it's innerHTML emptied are removed from memory

        //Move the element to the garbage bin element
        garbageBin.appendChild(element);
        //Empty the garbage bin
        garbageBin.innerHTML = "";
        }
    }

To use it in your context, you would do it like this:

discardElement(this);

Solution 2

This is more of an FYI than an actual answer, but it is also quite interesting.

From the W3C DOM core specification (http://www.w3.org/TR/DOM-Level-2-Core/core.html):

The Core DOM APIs are designed to be compatible with a wide range of languages, including both general-user scripting languages and the more challenging languages used mostly by professional programmers. Thus, the DOM APIs need to operate across a variety of memory management philosophies, from language bindings that do not expose memory management to the user at all, through those (notably Java) that provide explicit constructors but provide an automatic garbage collection mechanism to automatically reclaim unused memory, to those (especially C/C++) that generally require the programmer to explicitly allocate object memory, track where it is used, and explicitly free it for re-use. To ensure a consistent API across these platforms, the DOM does not address memory management issues at all, but instead leaves these for the implementation. Neither of the explicit language bindings defined by the DOM API (for ECMAScript and Java) require any memory management methods, but DOM bindings for other languages (especially C or C++) may require such support. These extensions will be the responsibility of those adapting the DOM API to a specific language, not the DOM Working Group.

In other words: memory management is left to the implementation of the DOM specification in various languages. You would have to look into the documentation of the DOM implementation in javascript to find out any method to remove a DOM object from memory, which is not a hack. (There is however very little information on the MDC site on that topic.)


As a note on jQuery#remove and jQuery#empty: from what I can tell neither of these methods does anything other than removing Objects from DOM nodes or removing DOM nodes from the document. They only remove That of course does not mean that there is no memory allocated to these objects (even though they aren't in the document anymore).

Edit: The above passage was superfluous since obviously jQuery cannot do wonders and work around the DOM implementation of the used browser.

Solution 3

Have you removed any event listeners? That can cause memory leaks.

Solution 4

The code below does not leak on my IE7 and other browsers:

<html>
<head></head>
<body>
    <a href="javascript:" onclick="addRemove(this)">add</a>
    <ul></ul>
    <script>
        function addRemove(a) {
            var ul = document.getElementsByTagName('UL')[0],
                li, i = 20000;
            if (a.innerHTML === 'add') {
                while (i--) {
                    li = document.createElement('LI');
                    ul.appendChild(li);
                    li.innerHTML = i;
                    li.onclick = function() {
                        alert(this.innerHTML);
                    };
                }
                a.innerHTML = 'remove';
            } else {
                while (ul.firstChild) {
                    ul.removeChild(ul.firstChild);
                }
                a.innerHTML = 'add';
            }
        }
    </script>
    </body>
</html>

May be you can try to spot some differences with your code.
I know that IE leaks far less when you insert first the node in the DOM before doing anything to it, eg: attaching events to it or filling its innerHTML property.

Share:
48,258
podeig
Author by

podeig

Updated on May 17, 2020

Comments

  • podeig
    podeig about 4 years

    My JavaSript code builds a list of LI elements. When I update the list, memory usage grows and never goes down. I tested in sIEve and it shows that the browser keeps all elements that were supposed to be deleted by $.remove() or $.empty jQuery commands.

    What should I do to remove DOM nodes without the memory leak?

    See my other question for the specific code.

  • podeig
    podeig over 13 years
    I even do for each element $(this).unbind().html("").remove();
  • podeig
    podeig over 13 years
    I did not know about "The DOM preserves all DOM nodes, even if they have been removed from the DOM tree itself" Do you mean $.remove() function does not do anything?
  • podeig
    podeig over 13 years
    How many MegaBite must be used to run garbage collector. I think 150Mb must be enough. But it does not work for me.
  • podeig
    podeig over 13 years
    After testing in sIEve can I say that it helps to remove garbage nodes. But when I test in IE8 and FF anyway memory usage grows. Andrew, what appendChild does? Moves a node to the garbage div? Should I run $.unbind() before discadElement()?
  • jayarjo
    jayarjo over 13 years
    I thought jQuery removes event handlers automatically, when you do element.remove(), no?
  • vsync
    vsync over 12 years
    from jQuery .remove() API: "...In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed. To remove the elements without removing data and events, use .detach() instead."
  • vsync
    vsync over 12 years
    You said that "from what I can tell neither of these methods does anything other than removing Objects from DOM nodes or removing DOM nodes from the document"..so I quoted from the API that it does more than that. it also remove all bound events.
  • FK82
    FK82 over 12 years
    The context was memory allocation. But oh well, there I striked out the passage on jQuery.
  • Guillermo Moscoso
    Guillermo Moscoso over 10 years
    Does the garbage bin need to be added to the document's body?
  • Max Wallace
    Max Wallace over 9 years
    This answer is confusing and ambiguous. It says that a page refresh is the 'only way' to remove nodes, yet it also implies that the browser's garbage collection will deallocate those nodes' memory. If true, then a refresh isn't the 'only' way. Moreover, from the perspective of the developer, there should be no distinction between objects that can be garbage collected, and objects that have been garbage collected-- that's the whole point of automatic memory management-- when the GC runs should be opaque to the user.
  • Max Wallace
    Max Wallace over 9 years
    Does "The DOM preserves all DOM nodes" mean that browsers implementing the DOM spec will actually store references to those nodes outside the DOM tree, or just that those nodes will hang around in memory until the next time the garbage collector runs? The answer implies the latter, but then those nodes aren't 'preserved' in any sense of the word if they're capable of being garbage collected.
  • Randy the Dev
    Randy the Dev over 9 years
    Hi Max, the problem is actually just bad browsers not cleaning up cross references properly, meaning the garbage collector can't collect them. This might have changed in the last 5 years.
  • Bobby Jack
    Bobby Jack over 4 years
    Both links broken; downvoting
  • Bobby Jack
    Bobby Jack over 4 years
    I've fixed the Crockford link, but I have no idea what the other one is
  • Ankur Parihar
    Ankur Parihar about 4 years
    Fixed closure link, using web archive
  • Fred
    Fred over 2 years
    Thank you @skilldrick! 10 years later, your suggestion put me on the right path (I'm doing vanilla JS; no jQuery involved) :-D