2010-05-31 6 views
2

Je ne peux tout simplement pas pour la vie de comprendre cette fuite de mémoire dans Internet Explorer.Problème de fuite/performance mémoire Javascript?

insertTags simple prend la chaîne str et place chaque mot dans les balises de début et de fin pour le HTML (généralement les balises d'ancrage). transliterate est pour les nombres arabes, et remplace les nombres normaux 0-9 avec un & # .. n; Identité XML pour leurs homologues arabes.

fragment = document.createDocumentFragment(); 
for (i = 0, e = response.verses.length; i < e; i++) 
{ 
    fragment.appendChild((function(){ 
     p = document.createElement('p'); 
     p.setAttribute('lang', (response.unicode) ? 'ar' : 'en'); 
     p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>'); 
     try { return p } finally { p = null; } 
    })()); 
} 
params[0].appendChild(fragment); 
fragment = null; 

J'aimerais quelques liens autres que MSDN et about.com, parce qu'aucun d'entre eux ont suffisamment expliqué pourquoi me fuites mon script mémoire. Je suis sûr que c'est le problème, car sans cela tout va vite (mais rien ne s'affiche). J'ai lu que faire beaucoup de manipulations DOM peut être dangereux, mais les boucles pour un maximum de 286 fois (# de versets dans la sourate 2, la sourate la plus longue dans le Coran).

* fuites de mémoire dans IE7 et IE8, ne suis pas sûr 6, mais fonctionne parfaitement bien dans Safari 4, FF 3.6, Opera 10.5, Chrome 5 ... *

+0

Je ne sais pas quelle version d'IE que vous utilisez, mais j'ai eu des problèmes de performance dans le passé avec IE7..Using le même code sur IE8/FF/Chrome a été rapide, mais IE7 n'aime pas beaucoup de traverser le DOM –

Répondre

6

Les variables sont limitées aux fonctions, pas si/else/for/while/etc. des blocs. Chaque fois que vous appelez

fragment.appendChild((function() { ... 

vous créez une nouvelle fonction (nouvelle portée). Cette nouvelle fonction fait référence aux variables i et response. Alors maintenant, i et response sont étendues à la fois la fonction externe et la nouvelle fonction.

Cela ne suffit pas pour fuir la mémoire. (i et response sont des variables normales qui vont sortir du champ après la nouvelle fonction est fait)

MAIS, vous créez un élément p DOM dans la nouvelle fonction, et de référence dans la fonction externe (vous le retourner à l'appel fragment.appendChild en tant qu'argument). Maintenant, pensez-y: vous avez la portée externe fragment référençant un DOM p créé à partir de la portée interne, qui devait utiliser les variables i et response de la portée externe pour créer l'élément DOM en premier lieu.

Les objets DOM fragment et p DOM ont chacun une référence les uns aux autres. Malgré vos tentatives pour mettre à zéro le nombre de références en annulant les pointeurs de variable, p=null et fragment = null ne se débarrasseront pas de toutes les références. Le fragment a toujours une référence à l'intérieur p, qui a toujours une référence à la variable externe response. Les deux "étendues" ne seront jamais collectées à cause de cette dépendance circulaire restante.

Toute personne, s'il vous plaît me corriger si je l'ai fait des erreurs.


Comme pour une solution, n'utilisez pas de fonction interne!

fragment = document.createDocumentFragment(); 
for (var i = 0, var e = response.verses.length; i < e; i++) 
{ 
    var p = document.createElement('p'); 
    p.setAttribute('lang', (response.unicode) ? 'ar' : 'en'); 
    p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>'); 
    fragment.appendChild(p); 
} 
params[0].appendChild(fragment); 
+1

Même conclusion, meilleure explication. Je crois que vous * ne devriez pas utiliser 'var p' dans la boucle, puisque vous ne devez pas déclarer à nouveau une variable existante. – deceze

+0

Le var à l'intérieur de la boucle n'a pas d'effets secondaires. La déclaration sera hissée au sommet par l'analyseur. L'avantage de déclarer où est utilisé est juste, c'est clair où il est utilisé. –

+0

Merci, mais cela conduit au même résultat. J'utilise pour utiliser ce code, mais a trouvé la fonction interne réelle allégé la charge pour IE un peu! Je ne comprends pas. – Tom

-2

Je ne peux pas vraiment vous dire pourquoi IE prétend qu'il fuit la mémoire, mais ce code est sacrément compliqué pour ce qu'il fait. Et cette ligne semble hautement suspecte et superflue: try { return p } finally { p = null; }.

Que diriez-vous simplifier un peu et la portée des variables:

var fragment = document.createDocumentFragment(); 
var p, t; 
for (var i = 0; i < response.verses.length; i++) 
{ 
    p = document.createElement('p'); 
    if (response.unicode) { 
     p.setAttribute('lang', 'ar'); 
     t = (response.surah + ':' + (i+1)).transliterate(); 
    } else { 
     p.setAttribute('lang', 'en'); 
     t = response.surah + ':' + (i+1); 
    } 
    p.innerHTML = t + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>'); 
    fragment.appendChild(p); 
} 
params[0].appendChild(fragment); 
fragment = p = t = null; // likely unnecessary if they go out of scope anyway 

C'est encore beaucoup d'opérations DOM mais, ce qui peut prendre un certain temps sur les moteurs lents Javascript.

+0

http://geekswithblogs.net/FrostRed/archive/2008/11/29/127440.aspx c'est pourquoi j'utilise try {} finally {}, c'est une façon supposée de prévenir les fuites de mémoire, et ça aide de mon débogage dans IE8, mais pas assez. Le code ci-dessus n'est pas non plus équivalent. – Tom

+0

@Tom Vous avez raison, j'ai incorrectement démonté votre opérateur ternaire. Je l'ai réparé. Si vous n'utilisez pas une fonction comme un moyen de construire l'élément 'p', vous ne devriez pas commencer dans une situation comme décrit dans cet article pour commencer. – deceze

+1

@Tom: Message très utile, merci. Mais c'est le premier commentaire qui laisse entrevoir ce qui pourrait être votre problème. Jetez un coup d'oeil: **** Vous voudrez réviser votre code. IE a un bug où il n'exécutera pas votre instruction finally à moins que vous fournissiez un catch. voir ici pour plus de détails. http://webbugtrack.blogspot.com/2007/11/bug-184-catch-to-try-catch-finally-in.html Celui-ci m'a fait tirer les cheveux pendant des jours! l'homme que je déteste debuggin dans IE! **** –

0

Y a-t-il une fuite si vous supprimez l'attribut onclick du lien?

Vous pouvez essayer de supprimer l'onclick répété et de le remplacer par une délégation d'événement.

De plus, toutes vos variables semblent être dans une portée globale - cela ne devrait pas être aussi mauvais que de provoquer les problèmes que vous voyez, mais vous devriez corriger cela indépendamment.

+0

La suppression de l'onclick aide, mais il a encore une fuite importante autrement. Et non, rien n'est dans une portée globale, cela fait partie d'une fonction appelée pour gérer une réponse Ajax, et tout est hors de portée dès qu'elle est terminée. – Tom

2

Bien que la réponse a été acceptée, je pense que cela pourrait pourrait aussi avoir fait le travail:

var fragment = document.createDocumentFragment(); 

for (var i = 0, e = response.verses.length; i < e; i++) { 
    fragment.appendChild((function(p){ // Create a scope here itself :) 
     p = document.createElement('p'); // ?? without above, it was a global scope 
     p.setAttribute('lang', (response.unicode) ? 'ar' : 'en'); 
     p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>'); 
     try { return p } finally { p = null; } 
    })()); 
} 
params[0].appendChild(fragment); 
fragment = null; 

Motif de fuite de mémoire: Une fermeture en cours de création et retourné dans la fonction anonyme, puis maintenu en vie mais pas des ordures collectées depuis fragment l'utilise

donc, la solution peut être aussi simple que de fournir un champ lexical, spectacle n ci-dessus