IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Les fuites de mémoire


Sujet :

JavaScript

  1. #1
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut Les fuites de mémoire
    Salut,

    J'ouvre ce fil (notamment pour éviter les HS) suite à ce fil Utilisation de innerHTML dans lequel le sujet des "fuites de mémoire" à été évoqué...

    J'avais posté ici #7* deux liens vers deux articles qui traitent de la question mais je ne sais pas si ils concernent seulement IE ou si ils sont toujours d'actualité...

    Faut-il s'en remettre aux ramasse-miettes ou bien il faudrait l'aider dans certains cas ?

    Qu'en pensez-vous ?

    EDIT : J'avais aussi ouvert ce fil il y a quelques temps : Est-il possible de connaitre la consommation en mémoire d'une page web ? Comment la libérer ?

    * Ce message a reçu un pouce rouge pour je ne sais quelle raison, aucune explication n'a été donnée, dommage que la raison n'ait pas été donnée (elle était peut-être intéressante ? Cela aurait été peut-être plus constructif qu'un pouce rouge), mystère...

  2. #2
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 093
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 093
    Points : 6 754
    Points
    6 754
    Par défaut
    * Ce message a reçu un pouce rouge pour je ne sais quelle raison, aucune explication n'a été donnée, dommage que la raison n'ait pas été donnée (elle était peut-être intéressante ? En tous cela aurait été peut-être plus constructif qu'un pouce rouge), mystère...
    J’en connais quelques-un que le hors-sujet agace, je vais avancer l’hypothèse que c’est à cause de ça … Mais ce n’est qu’une hypothèse.

    À propos des liens que tu as partagés, Avoiding Memory Leaks est un peu trop vieux (2009). Bien qu’il ne le dise pas clairement, je pense qu’il fait référence au problème des références circulaires sous IE8-.

    En revanche, l’autre lien Memory leaks and memory management in JavaScript est intéressant, et j’ai pris le temps de le lire plusieurs fois. Il présente des cas de figure concrets où des fuites de mémoire peuvent se produire, avec à chaque fois un point commun : ce n’est pas un bug du navigateur, c’est le site web ou l’application qui est mal codée.

    Le passage sur eval m’a amusé, décidemment cette fonction n’a pas fini de nous montrer qu’elle est evil

    Je réalise que c’est vraiment compliqué d’avoir tous ces détails à l’esprit quand on code :
    • Le fait que les liens entre nœuds DOM sont toujours à double sens (le sens child et le sens parent ;
    • Les trucs pas forcément évidents qui font que des références ne sont pas nettoyées, par exemple setInterval ;
    • la mécanique des références au sein d’une closure (en fait, les mécaniques, car ça dépend de l’implémentation) ;
    • etc.

    D’ailleurs à propos de closures, je suis tombé sur ce billet de 2013 : An interesting kind of JavaScript memory leak.

    Pour conclure, c’est une bonne chose d’être prudent et de mettre des grosObjet = null aux endroits stratégiques, juste par précaution.


    Je reviens sur ce code de jQuery que j’avais posté dans l’autre conversation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      empty: function() {
        var elem,
          i = 0;
     
        for ( ; ( elem = this[ i ] ) != null; i++ ) {
          if ( elem.nodeType === 1 ) {
     
            // Prevent memory leaks
            jQuery.cleanData( getAll( elem, false ) );
     
            // Remove any remaining nodes
            elem.textContent = "";
          }
        }
     
        return this;
      },
    L’appel à .cleanData() supprime les données que jQuery a attachées à l’élément. Ça inclut celles ajoutées par l’utilisateur via .data(), mais aussi celles ajoutées en interne par jQuery, notamment les gestionnaires d’évènements. Je ne sais pas exactement pourquoi jQuery fait ça.

    On peut voir comment ça se passe avec un simple script :
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    <button>Le bouton</button>
    Code js : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $("button").on("click", function () {
      console.log("Le clic");
    }

    Dans la console, déclare une référence sur l’élément DOM nu (non jQuery) par exemple comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    let bouton = document.querySelector("button")
    Puis, commence à taper bouton.j et laisse faire l’autocomplétion. Tu verras qu’il y a une propriété avec un nom aléatoire (dans mon cas j’ai eu bouton.jQuery331059752775723594121) et que cette propriété pointe vers un objet. C’est cet objet qui est nettoyé par .cleanData().

    La doc de .data() indique que c’est pour prévenir les références circulaires :

    The jQuery.data() method allows us to attach data of any type to DOM elements in a way that is safe from circular references and therefore from memory leaks.
    https://api.jquery.com/jQuery.data/
    Mais d’après le MDN, les ramasse-miettes de tous les navigateurs depuis 2012 utilisent la stratégie mark and sweep avec laquelle les références circulaires ne sont plus un problème.

    As of 2012, all modern browsers ship a mark-and-sweep garbage-collector.
    https://developer.mozilla.org/en-US/...ory_Management
    Alors, est-ce que jQuery fait ainsi juste pour les anciens navigateurs ? Normalement non, car il est clairement indiqué que la version majeure 3 n’est compatible qu’à partir d’IE9+.

    Ça peut être un oubli, ou alors un bout de code qu’ils ont préféré ne pas retirer. Ou encore c’est juste par précaution.
    Pour l’instant, je n’ai pas d’explication à ce mystère et je vais continuer à chercher.

    Pour répondre à cette question :
    Est-il possible de connaitre la consommation en mémoire d'une page web ? Comment la libérer ?
    Danielhagnoul a déjà fourni quelques liens bien utiles, mais je rajoute que les consoles des différents navigateurs offrent aujourd’hui des outils satisfaisants pour examiner la mémoire d’une page web. Même s’il faut préciser que celle de Firefox n’est pas encore aussi facile d’utilisation que celle de Chrome.

    Un guide d’utilisation des outils mémoire de Chrome : Fix memory problems.

    J’ai moi-même mené quelques tests sous Firefox pour tenter de mettre jQuery en échec, et j’ai cru avoir réussi dans un premier temps, je voyais la mémoire de la page augmenter petit à petit. C’était un piège : le ramasse-miette dormait. Et puis il s’est réveillé d’un coup et il a tout supprimé.
    Ça m’a surpris car je pensais que le ramasse-miettes de Firefox était incrémental, mais visiblement il y a quelque chose que j’ai compris de travers
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  3. #3
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Merci pour cette réponse détaillée, je pense que cela sera utile aussi à d'autres (notamment la liste des points auxquels il faut prêter attention...

    Citation Envoyé par Watilin Voir le message
    Pour conclure, c’est une bonne chose d’être prudent et de mettre des grosObjet = null aux endroits stratégiques, juste par précaution.
    Oui et justement si on reprend l'exemple avec le DOM où on a ce code JS :

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
            var myDemoObject = {
                element: document.querySelector('#toRemove'),
                child: {
                    element: document.querySelector('#toRetain')
                }
            };
     
            // Removing DOM sub-tree after some user interactions
            myDemoObject.element.parentNode.removeChild(myDemoObject.element);
            // Clearing the reference to this DOM, it's not required anymore
            myDemoObject.element = null;

    Pour éviter le problème de fuite il faudrait faire myDemoObject.child.element = null ;*, non ? Et à priori je placerais cette instruction avant myDemoObject.element.parentNode.removeChild(myDemoObject.element); mais peut-être que ça marche aussi si on la place après ?

    * On pourrait peut-être faire carrément ça : myDemoObject = null ; ? Mais là on ne pourrait pas le placer avant l'instruction myDemoObject.element.parentNode.removeChild(myDemoObject.element);...


    Citation Envoyé par Watilin Voir le message
    Je reviens sur ce code de jQuery que j’avais posté dans l’autre conversation :
    Merci pour l'explication.

    Citation Envoyé par Watilin Voir le message
    Un guide d’utilisation des outils mémoire de Chrome : Fix memory problems.
    Merci pour ce lien.
    Citation Envoyé par Watilin Voir le message
    C’était un piège : le ramasse-miette dormait. Et puis il s’est réveillé d’un coup et il a tout supprimé.
    J'ai remarqué aussi ce problème de ramasse-miettes qui dort et se réveille tout à coup, c'est un problème je trouve dans certains cas...

  4. #4
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Je déplace ici le passage qui répond à la remarque de NoSmoking faite ici : #6

    ----------

    Par contre il y a une chose qui rejoint le sujet du fil Les fuites de mémoire, j'ai testé la remarque de NoSmoking :
    Citation Envoyé par NoSmoking Voir le message
    Ceci étant ton élément substitué existe toujours comme tu as une référence à celui-ci, dans ton cas var span2, tu donc peux encore l'utiliser.
    Effectivement avec ce code :

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var span2 = document.getElementById('span2');
    span2.parentNode.removeChild(span2);
     
    var newSpan2 = document.createElement('span');
    newSpan2.textContent = 'new span 2';
    var span3 = document.getElementById('span3');
     
    span3.parentNode.insertBefore(newSpan2, span3);
     
    console.log(span2)
    La span#span2 est bien affichée donc l'instruction span2.parentNode.removeChild(span2); n'a pas libéré la mémoire occupée par la span#span2 ???

    Pourtant dans l’article Memory leaks and memory management in JavaScript* il est dit que faire span2 = null ; n'était pas requis mais apparemment ça l'est ???


    * J'ai adapté bien sûr mais dans l'article on a ("Clearing the reference to this DOM, it's not required anymore") :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Removing DOM sub-tree after some user interactions
    myDemoObject.element.parentNode.removeChild(myDemoObject.element);
    // Clearing the reference to this DOM, it's not required anymore
    myDemoObject.element = null;

  5. #5
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 093
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 093
    Points : 6 754
    Points
    6 754
    Par défaut
    Citation Envoyé par Beginner. Voir le message
    Pourtant dans l’article Memory leaks and memory management in JavaScript* il est dit que faire span2 = null ; n'était pas requis mais apparemment ça l'est ???


    * J'ai adapté bien sûr mais dans l'article on a ("Clearing the reference to this DOM, it's not required anymore")
    Tu as traduit trop vite :
    // on supprime la référence DOM, elle n’est plus nécessaire
    Ce n’est pas la suppression qui n’est plus nécessaire (double négation, aoutch), c’est la référence. Ça change complètement le sens !

    D’autre part, l’article explique clairement qu’une variable globale est GC-root, c’est-à-dire considérée comme une racine pour le ramasse-miettes ; et que c’est une des raisons pour lesquelles il faut éviter les variables globales quand c’est possible. Dans la plupart des situations, on a au moins une fonction autour de nos variables, à commencer par le gestionnaire DOMContentLoaded / $(document).ready(), et quand cette fonction se termine, ses variables locales deviennent hors de portée et peuvent être nettoyées.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  6. #6
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Citation Envoyé par Watilin Voir le message
    Tu as traduit trop vite :
    Ce n’est pas la suppression qui n’est plus nécessaire (double négation, aoutch), c’est la référence. Ça change complètement le sens !
    Oui merci, c'est bien vu ! Donc pour l'auteur on fait cela car on a plus besoin de la référence, j'aurais préféré qu'il dise que c'est même carrément nécessaire pour libérer la mémoire...

    Citation Envoyé par Watilin Voir le message
    D’autre part, l’article explique clairement qu’une variable globale est GC-root, c’est-à-dire considérée comme une racine pour le ramasse-miettes ; et que c’est une des raisons pour lesquelles il faut éviter les variables globales quand c’est possible. Dans la plupart des situations, on a au moins une fonction autour de nos variables, à commencer par le gestionnaire DOMContentLoaded / $(document).ready(), et quand cette fonction se termine, ses variables locales deviennent hors de portée et peuvent être nettoyées.
    Merci. Oui cela fait une autre raison d'éviter les variables globales...

    Et tu abordes une autre de mes interrogations : sommes-nous d'accord qu'il n'y a pas besoin de faire span2 = null ; si span2 est une variable locale ? C'est-à-dire que quand tu dis "...et peuvent être nettoyées" c'est sous-entendu nettoyées automatiquement par le ramasse-miettes ?

  7. #7
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 093
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 093
    Points : 6 754
    Points
    6 754
    Par défaut
    Citation Envoyé par Beginner. Voir le message
    Et tu abordes une autre de mes interrogations : sommes-nous d'accord qu'il n'y a pas besoin de faire span2 = null ; si span2 est une variable locale ?
    Ça dépend. Il pourrait y avoir des conditions, comme dans l’article “An interesting kind of JavaScript memory leak”, qui font que les variables ne sont pas libérées, à cause de closures ou d’autre chose. Donc par précaution, si une variable est directement ou indirectement liée à un gros volume de données, il vaut mieux la supprimer.

    quand tu dis "...et peuvent être nettoyées" c'est sous-entendu nettoyées automatiquement par le ramasse-miettes ?
    Oui, mais pour être plus précis, je veux dire qu’elles sont désormais nettoyables par le ramasse-miettes, et qu’elles seront donc nettoyées à un certain moment ; on ne sait juste pas exactement à quel moment.
    La FAQ JavaScript – Les cours JavaScript
    Touche F12 = la console → l’outil indispensable pour développer en JavaScript !

  8. #8
    Membre expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 873
    Points : 3 717
    Points
    3 717
    Par défaut
    Merci.

Discussions similaires

  1. Détecter les fuites de mémoire sous Windows
    Par Nadawoo dans le forum Débuter
    Réponses: 1
    Dernier message: 25/11/2010, 00h26
  2. Outils pour detecter les fuite de mémoire
    Par sese12345 dans le forum Windows Mobile
    Réponses: 2
    Dernier message: 31/03/2010, 10h10
  3. [débutant] comment éviter les fuites de mémoire ?
    Par dahtah dans le forum Général Java
    Réponses: 6
    Dernier message: 13/03/2007, 17h40
  4. Comme intercepter les fuites de mémoire sous VS?
    Par Gabrielly dans le forum Visual C++
    Réponses: 4
    Dernier message: 18/09/2006, 20h57
  5. Réponses: 8
    Dernier message: 17/10/2002, 12h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo