307 votes

Si un élément DOM est supprimé, ses écouteurs sont-ils également supprimés de la mémoire?

Si un élément DOM est supprimé, ses écouteurs sont-ils également supprimés de la mémoire?

277voto

dsg Points 26355

Les navigateurs modernes

Si un élément du DOM qui est retiré de référence-libre (pas de références pointant vers celui-ci), alors oui - l'objet lui-même est capté par le garbage collector ainsi que des gestionnaires d'événements/auditeurs associés.

var a = document.createElement('div');
var b = document.createElement('div');
// Add event listeners to b etc...
a.appendChild(b);
a.removeChild(b);
b = null; 
// A reference to 'b' no longer exists 
// Therefore the object and any event listeners attached to it are removed.

Cependant, si il y a des références qui pointe toujours vers l'objet, l'objet et ses écouteurs d'événements sont conservés en mémoire.

var a = document.createElement('div');
var b = document.createElement('div'); 
// Add event listeners to b etc...
a.appendChild(b);
a.removeChild(b); 
// A reference to 'b' still exists 
// Therefore the object and any associated event listeners are still retained.

Concernant jQuery; jQuery remove() méthode est réellement mis en œuvre à l'aide de .removeChild() - les mêmes règles s'appliquent à elle comme ci-dessus en ce qui concerne les références, etc.


Les navigateurs plus anciens

Les anciens navigateurs - en particulier les anciennes versions d'IE - sont connus pour avoir des problèmes de fuite de mémoire due à des écouteurs d'événement garder tenir de références à des éléments qu'ils étaient attachés.

Les causes les plus fréquentes de ces fuites inclure des Références Circulaires, des Fermetures et de la Croix-page fuites.

Si vous voulez une explication plus approfondie des causes, des motifs et des solutions utilisées pour fixer héritage de la version de IE des fuites de mémoire, je vous recommande la lecture de cet article MSDN sur la Compréhension et la Résolution d'Internet Explorer Fuite de Modèles.

Quelques autres articles qui peuvent vous intéresser:

Retirer manuellement les auditeurs vous-même serait probablement une bonne habitude à prendre dans ce cas (et seulement si la mémoire est indispensable à votre demande et vous sont effectivement ciblage de ces navigateurs).

21voto

Sreenath S Points 1024

concernant jQuery:

l' .méthode remove() prend des éléments de la DOM. Utiliser .remove() lorsque vous souhaitez supprimer l'élément lui-même, ainsi comme tout à l'intérieur. Outre les éléments eux-mêmes, tous lié aux événements et jQuery données associées avec les éléments sont supprimés. Pour supprimer les éléments sans supprimer les données et les événements, utiliser .detach() au lieu de cela.

Référence: http://api.jquery.com/remove/

jQuery v1.8.2 .remove() code source:

remove: function( selector, keepData ) {
    var elem,
        i = 0;

    for ( ; (elem = this[i]) != null; i++ ) {
        if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
            if ( !keepData && elem.nodeType === 1 ) {
                jQuery.cleanData( elem.getElementsByTagName("*") );
                jQuery.cleanData( [ elem ] );
            }

            if ( elem.parentNode ) {
                elem.parentNode.removeChild( elem );
            }
        }
    }

    return this;
}

apparemment, jQuery utilise node.removeChild()

Selon ce : https://developer.mozilla.org/en-US/docs/DOM/Node.removeChild ,

The removed child node still exists in memory, but is no longer part of the DOM. You may reuse the removed node later in your code, via the oldChild object reference.

ie écouteurs d'événement peut obtenir supprimé, mais node existe toujours dans la mémoire.

9voto

NVI Points 3846

Oui

entrer la description de l'image ici

Notez que je dois appuyer deux fois sur l'icône "forcer GC" en raison d'un bug dans Chrome DevTools. Mon gif précédent était trompeur à cause de cela.

leak-memory.html:

 <script>
function run() {

    function clicked(e) {
        console.log(e);
    }

    for (var i = 0; i < 1000; i++) {
        var span = document.createElement('span');
        span.addEventListener('click', clicked, false);
        document.body.appendChild(span);
    }

    setTimeout(function() {
        var spans = document.querySelectorAll('span');
        for (var i = 0; i < spans.length; i++) {
            var span = spans[i];
            span.parentNode.removeChild(span);
        }
    }, 100);

}
</script>
<button onclick="run()">Run (memory leak)</button>
 

no-memory-leak.html

 <script>
function run() {

    function clicked(e) {
        console.log(e);
    }

    for (var i = 0; i < 1000; i++) {
        var span = document.createElement('span');
        span.addEventListener('click', clicked, false);
        document.body.appendChild(span);
    }

    setTimeout(function() {
        var spans = document.querySelectorAll('span');
        for (var i = 0; i < spans.length; i++) {
            var span = spans[i];
            span.removeEventListener('click', clicked, false);
            span.remove();
        }
    }, 100);

}
</script>
<button onclick="run()">Run (no memory leak)</button>
 

7voto

lib3d Points 1679

N'hésitez pas à regarder des tas de voir les fuites de mémoire dans les gestionnaires d'événements garder une référence à l'élément de fermeture et l'élément de garder une référence pour le gestionnaire d'événements.

Garbage collector n'aime pas les références circulaires.

D'habitude fuite de mémoire cas: admettre qu'un objet a une ref à un élément. Cet élément a une ref pour le gestionnaire. Et le gestionnaire a une référence à l'objet. L'objet a refs pour beaucoup d'autres objets. Cet objet faisait partie d'une collection que vous pensez que vous avez jetés par unreferencing à partir de votre collection. => l'objet entier et tout ce qu'il désigne restera en mémoire jusqu'à la page de sortie. => vous devez penser à une complète méthode de mise à mort pour un objet de la classe ou de la confiance d'un framework mvc par exemple.

Par ailleurs, n'hésitez pas à utiliser la Retenue de l'arbre une partie de Chrome dev tools.

2voto

Darin Dimitrov Points 528142

Oui, le ramasse-miettes les supprimera également. Peut-être pas toujours le cas avec les navigateurs hérités cependant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X