Javascript, références circulaires et fuites de mémoire

D'après ce que je me souviens d'un passé pas trop lointain, les interprètes Javascript ont souffert de problèmes de fuite de mémoire lorsqu'ils étaient confrontés à des références circulaires.

Est-ce toujours le cas dans les derniers navigateurs? (P. Ex. Chrome, FF 3.5, etc.)

La grande majorité des fuites dont nous parlons avec JavaScript sont spécifiquement dans IE6-7 lorsque vous effectuez une boucle de référence entre les objets JavaScript et les objets hôtes tels que les nœuds DOM.

Dans IE6, cela est particulièrement pernicieux en ce sens que vous ne récupérez pas la mémoire lorsque vous quittez la page; Il est parti jusqu'à ce que vous quittez le navigateur. Dans IE7, effacer la page renvoie maintenant la mémoire, mais vous pouvez toujours avoir des difficultés lorsque vous avez une application longue durée. IE8 résout la plupart de ce problème correctement en transformant les noeuds DOM en objets JavaScript natifs au lieu d'objets hôtes. (Vous pouvez encore déclencher les fuites dans IE8 en incluant d'autres objets non indigènes comme des objets ActiveX dans une boucle de référence.)

Il y aura toujours de petites fuites de mémoire obscures qui se cachent dans des endroits aléatoires pour tous les navigateurs, en particulier dans les anciennes versions. Mais il n'y a aucun moyen de catégoriser facilement et de les éviter comme avec le problème IE Refloop.

Pour ajouter à la réponse Bobine , j'ai fait quelques tests avec IE8 .

J'ai essayé presque tous les exemples fournis à http://www.javascriptkit.com/javatutors/closuresleak/index.shtml

Aucun d'entre eux ne gagne de mémoire (au moins pas d'une manière perceptible), à ​​l'exception de l'exemple qui supprime les nœuds enfants avec des événements qui leur sont encore attachés .

Ce type d'exemple, je pense qu'il est mieux expliqué par Douglas Crockford dans son queuetest2.

Celui-ci continue de perdre de la mémoire sur IE8 et il est assez facile à tester en exécutant simplement le script de test et en regardant Windows Task Manager – Performance – PF Usage. Vous verrez que l'utilisation de PF augmente de près de 1 Mo par boucle (très rapide).

Mais dans IE8, la mémoire est relâchée en téléchargement de page (comme la navigation vers une nouvelle page ou le rechargement de la même page ) et évidemment aussi lorsque vous fermez complètement le navigateur.

Donc, pour qu'un utilisateur final puisse percevoir ces fuites de mémoire sur IE8 (en tant que performances systerm réduites), il doit rester sur la même page depuis longtemps, ce qui, en ce moment, peut souvent se produire avec AJAX, mais cette page a également besoin Pour faire des centaines d'éléments d'élimination des éléments avec l'événement qui leur est rattaché .

Le test de Douglas Crockford met l'accent sur le navigateur avec plus de 100 nœuds ajoutés puis supprimés , ce qui est excellent pour vous montrer le problème, mais dans la vie réelle, je n'ai jamais eu de page qui a supprimé plus de 10 éléments. INMHO habituellement, il est plus rapide à utiliser l' display: none plutôt que de supprimer un ensemble entier de noeuds, c'est pourquoi je n'utilise pas removeChild autant.


Pour ceux qui pourraient être plus intéressés par la fuite de mémoire IE8 expliqué ci-dessus, j'ai fait un autre test et il semble que les fuites de membres ne s'affichent pas du tout dans IE8 lorsque vous utilisez innerHTML au lieu d' appendChild/removeChild pour ajouter / supprimer des éléments enfants avec des événements attachés. Donc, apparemment, la fonction de purge de Douglas Crockford (suggérée par lui pour empêcher les fuites de mémoire dans IE) n'est plus nécessaire dans IE8 au moins lors de l'utilisation de innerHTML

( EDITÉE grâce au commentaire 4esn0k ci-dessous ) … de plus, la fonction de purge de Douglas Crockford ne fonctionne pas du tout sur IE8, dans son code var a = d.attributes renvoie NO onclick attributs (ou tous les autres attributs onevent ) qui ont été ajoutés au moment de l'exécution sur IE8 (ils sont retournés sur IE7).

Douglas Crockford dit:

"La fonction de purge doit être appelée avant de supprimer n'importe quel élément, soit par la méthode removeChild, soit par la définition de la propriété innerHTML ".

Je donne ici le code du test:

 <body> <p>queuetest2 similar to the one provided by Douglas Crockford at http://www.crockford.com/javascript/memory/leak.html <br>but this one adds/removes spans using innerHTML instead of appendChild/removeChild.</p> <div id="test"></div> <script> /* ORIGINAL queuetest2 CODE FROM DOUGLAS CROCKFORD IS HERE http://www.crockford.com/javascript/memory/queuetest2.html */ (function (limit, delay) { var n = 0; var add = true; function makeSpan(n) { var div = document.getElementById('test'); //adding also an inline event to stress more the browser div.innerHTML = "<span onmouseover=\"this.style.color = '#00ff00';\">" + n + "</span>"; var s = div.getElementsByTagName('span')[0]; s.onclick = function(e) { s.style.backgroundColor = 'red'; alert(n); }; return s; } function process(n) { if(add) s = makeSpan(n); else s.parentNode.innerHTML = ""; //removing span by clearing the div innerHTML add = !add; } function loop() { if (n < limit) { process(n); n += 1; setTimeout(loop, delay); } } loop(); })(10000, 10); </script> </body> 

En ce qui concerne Internet Explorer 8 – ils disent qu'ils l'ont réparé dans MS IE8: http://msdn.microsoft.com/en-us/library/dd361842(VS.85).aspx

Un thread similaire ici sur StackOverflow: Savez-vous ce qui peut provoquer des fuites de mémoire en JavaScript?