web-dev-qa-db-fra.com

Recherche d'une fuite de mémoire JS dans chrome dev tools

J'utilise les outils de développement chrome dev) pour déterminer s'il y a une fuite de mémoire dans un code JS. Le chronologie de la mémoire a l'air bien avec la mémoire récupérée comme prévu.

enter image description here

Cependant, le instantané de la mémoire est déroutant car il semble qu'il y ait une fuite car il y a des entrées sous "Arborescence DOM détachée".

Est-ce que le truc sous "Arbre DOM détaché" attend juste d'être ramassé ou est-ce que ce sont de vraies fuites?

Est-ce que quelqu'un sait aussi comment savoir quelle fonction tient à une référence à un élément détaché?

enter image description here

41
Carl Rippon

Ces éléments sont référencés dans votre code mais ils sont déconnectés de l'arborescence DOM principale de la page.

Exemple simple:

var a = document.createElement("div");

a fait référence à un élément déconnecté maintenant, il ne peut pas être GC lorsque a est toujours dans la portée.

Si les arbres dom détachés persistent en mémoire, vous gardez des références à eux. C'est un peu facile avec jQuery de faire cela, il suffit de sauvegarder une référence à un résultat parcouru et de la conserver. Par exemple:

var parents = $("span").parent("div");
$("span").remove();

Maintenant, les travées sont référencées même s'il ne semble pas que vous les référeniez de toute façon. parents conserve indirectement les références à toutes les étendues via jQuery .prevObject propriété. Alors faire parents.prevObject donnerait l'objet qui référence toutes les travées.

Voir l'exemple ici http://jsfiddle.net/C5xCR/6/ . Même s'il ne semble pas directement que les travées seraient référencées, elles sont en fait référencées par la variable globale parents et vous pouvez voir que les 1000 travées dans l'arborescence DOM détaché ne disparaissent jamais.

Maintenant, voici le même jsfiddle mais avec:

delete parents.prevObject

Et vous pouvez voir que les travées ne sont plus dans l'arbre de dom détaché, ni n'importe où d'ailleurs. http://jsfiddle.net/C5xCR/7/

34
Esailija

Est-ce que le truc sous "Arbre DOM détaché" attend juste d'être ramassé ou s'agit-il de vraies fuites?

Avant de prendre un instantané, le navigateur exécutera le garbage collection et balaiera tous les objets qui ne sont pas référencés. Ainsi, l'instantané de tas ne contient toujours que des objets vivants. Par conséquent, si une arborescence DOM détachée se trouve dans l'instantané, il doit y avoir un élément dans l'arborescence référencé à partir de JavaScript.

Est-ce que quelqu'un sait aussi comment savoir quelle fonction tient à une référence à un élément détaché?

Il devrait y avoir un élément (ou plusieurs d'entre eux) dans la même arborescence DOM détachée qui a un fond jaune. Ces éléments sont référencés à partir du code JavaScript. Vous pouvez savoir qui garde exactement la référence à l'élément dans l'arborescence des rétentions.

15
Yury Semikhatsky

Depuis que vous avez ajouté la balise jQuery, j'ai eu un soupçon sournois qu'il s'agissait d'une chose jQuery . Un rapide google m'a amené à cette page . Lorsque vous utilisez la méthode detach de jQ, une référence à l'objet est toujours conservée en mémoire, ce qui peut être à l'origine de votre instantané.

Une autre chose pourrait être que jQuery a un nœud div à portée de main, qui est évidemment conservé en mémoire, mais jamais ajouté au domaine réel ... une sorte de document.createNode('div') sans l'ajouter . Ceci, aussi, apparaîtra dans l'instantané de la mémoire. Vous ne pouvez pas contourner cela, jQuery l'utilise pour analyser les chaînes en éléments html.

Donc, pour supprimer certains éléments de la mémoire, utilisez la méthode jQuery .remove(), et votre mémoire sera effacé instantanément. cf le commentaire d'Esailija sur la raison pour laquelle remove ne correspond pas tout à fait ici .
$('#someElem')[0].parentNode.removeChild($('#someElem')[0]); Doit supprimer complètement l'élément, mais peut ne pas dissocier les événements. Peut-être quelque chose du genre:

$('#someElem').detach();//to remove any event listeners
$('#someElem')[0].parentNode.removeChild($('#someElem')[0]);//remove element all together

Et, encore une fois, comme Esailija l'a souligné dans sa réponse, assurez-vous d'attribuer des références à n'importe quel objet jQuery (var someRef= $('.someSelector');) à une variable globale, car ils ne seront pas GC. Évitez simplement les globaux tous ensemble, en fait.
Mais pour répondre brièvement et clairement à votre question: non ce ne sont pas des fuites de mémoire réelles , la mémoire doit être libérée sur le onbeforeunload événement. L'objet jQuery est supprimé, donc toutes les références sont hors de portée. Du moins, c'est ce que ma "recherche" me porte à croire. Peut-être pas tout à fait pertinent, mais juste à titre de référence Voici une question sur les fuites de mémoire que j'ai postée il y a quelque temps, et avec elle quelques choses que j'ai découvertes ..

1
Elias Van Ootegem