web-dev-qa-db-fra.com

Comment supprimer des éléments DOM sans fuites de mémoire?

Mon code JavaSript crée une liste d'éléments LI. Lorsque je mets à jour la liste, l'utilisation de la mémoire augmente et ne diminue jamais. J'ai testé dans sIEve et cela montre que le navigateur conserve tous les éléments qui étaient censés être supprimés par $.remove() ou $.empty Commandes jQuery.

Que dois-je faire pour supprimer les nœuds DOM sans fuite de mémoire?

Voir mon autre question pour le code spécifique.

47
podeig

Le DOM conserve tous les nœuds DOM, même s'ils ont été supprimés de l'arborescence DOM lui-même, la seule façon de supprimer ces nœuds est de faire un rafraîchissement de page (si vous mettez la liste dans un iframe, le rafraîchissement ne sera pas aussi perceptible)

Sinon, vous pourriez attendre que le problème s'aggrave suffisamment pour que le ramasse-miettes des navigateurs soit forcé d'agir (en parlant ici de centaines de mégaoctets de nœuds inutilisés)

La meilleure pratique consiste à réutiliser les nœuds.

EDIT: Essayez ceci:

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 = "";
        }
    }

Pour l'utiliser dans votre contexte, vous le feriez comme ceci:

discardElement(this);
45
Randy the Dev

C'est plus une [~ # ~] fyi [~ # ~] qu'une réponse réelle, mais elle est aussi très intéressante.

À partir de la spécification du noyau DOM W3C (http://www.w3.org/TR/DOM-Level-2-Core/core.html):

Les API Core DOM sont conçues pour être compatibles avec un large éventail de langages, y compris les langages de script utilisateur général et les langages les plus difficiles utilisés principalement par les programmeurs professionnels. Ainsi, les API DOM doivent fonctionner dans une variété de philosophies de gestion de la mémoire, des liaisons de langage qui n'exposent pas du tout la gestion de la mémoire à l'utilisateur, en passant par celles (notamment Java) qui fournissent des constructeurs explicites mais fournissent un mécanisme de collecte automatique des ordures pour automatiquement récupérer la mémoire inutilisée, à ceux (en particulier C/C++) qui nécessitent généralement que le programmeur alloue explicitement la mémoire des objets, suit où elle est utilisée et la libère explicitement pour une réutilisation. Pour garantir une API cohérente sur ces plates-formes, le DOM ne résout pas du tout les problèmes de gestion de la mémoire, mais les laisse à la place pour l'implémentation. Aucune des liaisons de langage explicites définies par l'API DOM (pour ECMAScript et Java) ne nécessite de méthodes de gestion de la mémoire, mais les liaisons DOM pour d'autres langues (en particulier C ou C++) peuvent nécessiter une telle prise en charge. Ces extensions seront la responsabilité de ceux qui adaptent l'API DOM à une langue spécifique, et non le groupe de travail DOM.

En d'autres termes: la gestion de la mémoire est laissée à l'implémentation de la spécification DOM dans différents langages. Vous devriez regarder dans la documentation de l'implémentation DOM en javascript pour trouver une méthode pour supprimer un objet DOM de la mémoire, ce qui n'est pas un hack. (Il y a cependant très peu d'informations sur le site du MDC à ce sujet.)


Comme une note sur jQuery#remove et jQuery#empty: d'après ce que je peux dire, aucune de ces méthodes ne fait autre chose que la suppression de Objects du DOM nodes ou la suppression du DOM nodes du document. Ils ne font que supprimer Cela ne signifie bien sûr pas qu'il n'y a pas de mémoire allouée à ces objets (même s'ils ne sont plus dans le document).

Edit: Le passage ci-dessus était superflu car évidemment jQuery ne peut pas faire de merveilles et contourner l'implémentation DOM du navigateur utilisé.

13
FK82

Avez-vous supprimé des écouteurs d'événements? Cela peut provoquer des fuites de mémoire .

8
Skilldrick

Le code ci-dessous ne fuit pas sur mon IE7 et d'autres navigateurs:

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

Peut-être que vous pouvez essayer de repérer certaines différences avec votre code.
Je sais que IE fuit beaucoup moins lorsque vous insérez d'abord le nœud dans le DOM avant de lui faire quoi que ce soit, par exemple: y attacher des événements ou remplir son innerHTML propriété.

2
Mic

Si vous devez "post-corriger" une fuite et devez le faire sans réécrire tout votre code pour prendre en compte les fermetures, les références circulaires, etc., utilisez la méthode de purge Douglas Crockfords avant de supprimer:

http://javascript.crockford.com/memory/leak.html

Ou utilisez cette solution de contournement de fermeture:

http://laurens.vd.oever.nl/weblog/items2005/closures/

1
Jens