AJAX et fuites mémoires

Javascript/AJAX Ajouter un commentaire

Par le biais d’AJAX, l’utilisation intensive du Javascript a permis de soulever de nombreux problèmes de mémoire au sein des navigateurs du marché. Ni IE, ni Firefox ne sont épargnés par ce sujet qui empoisonne la vie des développeurs web. Petite explication de texte.

Typologie des fuites mémoires

Les fuites mémoires sont causées par des cas de code assez vicieux reliant le moteur JavaScript et le moteur DOM. Chaque moteur a son propre système de ramasse-miettes (Garbage Collector), provoquant des dégâts s’ils se référencent l’un l’autre.

Premier cas : supposons qu’un noeud DIV “a” fasse référence à un élément SPAN “b” imbriqué dedans. On a le code suivant :

var parent = document.getElementById('a');
var child = document.getElementById('b');
 
// On ajout un attribut "ref" aux deux noeuds
parent.ref = child;
child.ref = parent;

Second cas : il concerne des références d’un objet sur lui-même (appelé “expando”). Le noeud du DOM s’auto-référence via un de ses attributs :

// On crée un objet dans le moteur JScript à partir d'une référence DOM
var globalElmt = document.getElementById('a');
 
// On crée un référence dans le DOM à partir d'un objet du JScript
document.getElementById('a').expando = globalElmt;

Troisième cas : l’utilisation de fonctions “inline” associée à un noeud HTML, sur un évènement par exemple. C’est également une source de fuites, comme le montre l’exemple suivant :

function loadMyPage() {
   var elem = document.getElementById('myelement');
 
   elem.onclick = function () {
      window.alert('hi!');
   };
}

L’objet elem est ici crée dans le moteur JS ainsi qu’une fonction “anonyme” associée à l’évènement onclick. Cependant une fois sorti de la portée de la fonction, l’objet elem est laissé au Garbage collector dans le moteur JS, mais le moteur DOM lui conserve la référence entière avec la fonction associée. Ceci crée une référence DOM/JS qui provoque la fuite.

Quelles solutions de la part des éditeurs de logiciels ?

Pour IE, un fix a été proposé par Microsoft le 12/08/2007 et sera désormais disponible sous la forme d’une mise à jour pour IE6. Malheureusement, un développeur a déjà montré que ce correctif était incomplet.

Pour Firefox, le sujet est toujours en cours. En attendant vous disposez toujours d’un excellent add-on pour la détection de fuites mémoires.

Les corrections à apporter à votre code

La meilleur solution reste encore entre vos mains ! Voici quelques astuces pour éviter ou limiter les fuites.

1) Eviter autant que possible les références circulaires entre éléments. Une méthode plutôt simple et astucieuse consiste par exemple à référencer l’ID d’un élément sous forme d’une chaine plutôt que le noeud du DOM lui-même. Yahoo!UI l’utilise de façon assez intensive dans son framework et n’utilise les noeuds que lorsque c’est nécessaire.

2) Il est possible de refactoriser votre code dans certains cas. Par exemple, le cas des fonctions attachées à des évènements peut être corrigée très simplement. Le code présenté précédemment devient donc :

function maFonction(e) {
      window.alert('test');
}
 
function chargeMaPage() {
   var elem = document.getElementById('monelement');
   elem.onclick = maFonction;
}

3) Dans le cas où vous ne pouvez pas faire autrement, implémentez un Garbage Collector (GC) pour garder une trace de toutes vos références susceptibles de créer des fuites. Mootools propose par exemple un GC écrit en JS qui conserve cette fameuse trace et les nettoie en sortie de page.

Pour finir un peu de documentation (en anglais) qui a inspiré cet article :
http://msdn2.microsoft.com/en-us/library/bb250448.aspx
http://www.quirksmode.org/blog/archives/2005/02/javascript_memo.html
http://www-128.ibm.com/developerworks/web/library/wa-memleak/

4 Réponses to “AJAX et fuites mémoires”

  1. Tonio Says:

    Bon article, très instructif!
    Merci! :)

  2. Cédric Says:

    Même avis que Tonio : très instructif! On se croit facilement protégé par le fait qu’un script ne tourne pas longtemps, et que donc à sa fermeture la mémoire est libérée, mais les applis web 2.0 changent les pratiques.
    A noter que PHP4 a eu le même soucis quand php-gtk a permis de faire des applis qui ont un temps de vie supérieur à celui des pages web.

  3. Christophe Buguet Says:

    Merci de vos retours.

    @Cedric
    Pour PHP, ce que vous dites est tout à fait vrai mais à mon avis, il met plus en exergue une mauvais pratique des développeurs.
    PHP gérant la mémoire tout seul, on a vite tendance à oublier qu’il est important de “déréférencer” un tableau associatif, un objet ou une variable lorsqu’elle ne sert plus.
    Ceci est valable pour des scripts consommateurs, utilisant PHP-GTK comme vous l’avez mentionné, ou même un script PHP en ligne de commande (j’en développe assez souvent comme tâches cron).

  4. Olivier Says:

    Mmm… Il va falloir que je vérifie mais je crois qu’en lisant ce billet je viens de comprendre le pourquoi du comment de mon bug… Thanks!

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in