2010-11-12 4 views
6

Juste rencontré un client qui ont d'énormes problèmes de fuite de mémoire dans leur webapp Ajax. J'ai donc décidé de créer le testcase suivant pour démontrer le problème:Ajout d'éléments DOM avec jquery append() semble fuir la mémoire?

J'ai utilisé goutte à goutte/Sieve pour le profilage de la mémoire dans l'exemple ci-dessous (http://home.orange.nl/jsrosman/)

Le cas est simple: je le javascript suivant:

<html> 
    <head>  
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.min.js"> 
</script>  
</head>  
<script type="text/javascript"> 

    var lihtml = "<li class='green'>this is a test text</li>"; 

    function populatelist() { 
     for (var i = 0; i < 10000; i++) { 
      $('#listparent').append(lihtml); 
     }  
    }  

    function clearlist() { 
     $('#listparent').empty(); 
     if (typeof (CollectGarbage) == "function") { 
      alert('gc'); 
      CollectGarbage(); 
     }  
    } 


    /* Alternative clearlist with Remove instead of Empty(), still leaks */ 
    function clearlist() { 
     /* test remove the parent itself instead of empty below */ 
     $('#listparent').remove(); 
     $('body').append("<ul id='listparent'>");   
     //$('#listparent').empty(); 
     if (typeof (CollectGarbage) == "function") { 
      alert('gc'); 
      CollectGarbage(); 
     } 
    } 

    /* Edit!, this is the most effective way to release memory so far */ 
    function clearlist() { 
    $('#listparent').html(""); 
    if (typeof (CollectGarbage) == "function") { 
     alert('gc'); 
     CollectGarbage(); 
    } 
} 
</html> 
</script> 

<body> 
    <button onclick="javascript:populatelist()">Populate list</button> 
    <button onclick="javascript:clearlist()">Clear list</button> 
    <ul id="listparent"> 
     <li>kjjk</li> 
    </ul>  
</body> 

</html> 

Chaque clic sur la liste populate 10000 éléments ajoute li (représenté sous forme de texte). Clearlist appelle jquery empty() qui devrait supprimer le sous-arbre DOM et le rendre éligible pour GC. Donc, je lance ce cas dans sIEve et pour chaque fois que j'ajoute de nouveaux éléments, l'utilisation de la mémoire augmente, je ne l'ai jamais vu garbage collecter ou libérer de la mémoire. Pas même lorsque l'utilisation de la RAM atteint 1,5 Go et même si j'essaie d'appeler GC explicitement pour IE.

C'est le même symptôme que j'ai vu chez le client qui a utilisé Jquery Ajax pour les données de la liste au lieu de mon contenu statique obv. Est-ce que je crée le DOM dans le mauvais sens?

Quelqu'un peut-il me dire pourquoi ce n'est pas ramassé, je ne vois pas d'autres références aux éléments DOM pour savoir pourquoi ils ne devraient pas être collectés. Un autre comportement bizarre est que parfois, dans l'utilisation de la mémoire sIEve, augmente même lorsque je clique sur la liste vide (quand la méthode jquery empty() est appelée)?

Si quelqu'un a une entrée, je serais très heureux. UPDATE, j'ai essayé d'utiliser $ ('# listparent'). Html ("") à la place qui semble libérer le DOM correctement, au moins il est publié dans sIEve. Je suppose que c'est la meilleure solution jusqu'à maintenant, mais je n'ai aucune explication à pourquoi remove() et empty() ne semble pas fonctionner. Peut-être qu'ils ne fonctionnent que pour les éléments ajoutés statiquement?

+0

juste curieux, avez-vous essayé dans plusieurs navigateurs? – Prescott

+0

Aucun sIEve ne fonctionne que dans IE, n'ont pas essayé FFX, principalement parce que IE est le seul navigateur utilisé par les utilisateurs dans ce système. – user408346

+0

En fait $ ('# listparent').html (""); fonctionne beaucoup mieux que vide() et remove() et semble libérer tout le DOM. Je suis sans voix – user408346

Répondre

1

oui, vous pourriez faire une énorme amélioration smthg faire comme si

var lihtml = "<li class='green'>this is a test text</li>", 
    listring = ""; 

function populatelist() { 
     for (var i = 0; i < 10000; i++) { 
      listring += lihtml; 
     }  

     $('#listparent').append(listring); 

    }  
... 

accès DOM limite aussi peu que possible. La différence est que vous faites 1 seul ajout au lieu de 10 milliers d'appendices. Vous devez toujours éviter la manipulation DOM dans les cycles

Editer: au lieu de empty() avez-vous essayé d'enlever() le ul et de le recréer soudainement?

+0

C'est une bonne suggestion et je l'ai essayé, le problème persiste quand même, il fuit toujours et vide() ne semble pas libérer le DOM. Sérieusement commencer à perdre la foi en jquery. – user408346

+0

J'ai édité ma réponse –

+1

J'ai essayé remove(), il fuit toujours mais peut-être légèrement moins que vide() ce qui semble augmenter la mémoire plutôt que de la relâcher. Ce qui est vraiment bizarre, c'est que $ ('# listparent'). Html (""); fait le tour et libère le DOM entier et la mémoire RAM?!? – user408346

1

Je suggère à la chaîne annexant HTML, puis en ajoutant au DOM:

function populatelist() { 
    for (var i = 0; i < 10000; i++) { 
     //$('#listparent').append(lihtml); 
     lihtml += "<li class='green'>this is a test text</li>"; 
    }  
}  
populateList(); 
$('#listparent').append(lihtml); 
+0

Comme ci-dessous, bonne suggestion mais la fuite reste. – user408346

0
$('#listparent').html(""); 

œuvres où ni vide() ou supprimer() fait. J'aimerais savoir pourquoi. Je suppose que c'est une solution cependant.

/* Ceci est le moyen le plus efficace pour libérer de la mémoire à ce jour */

function clearlist() { 
    $('#listparent').html(""); 
    if (typeof (CollectGarbage) == "function") { 
     alert('gc'); 
     CollectGarbage(); 
    }