Résoudre les problèmes de mémoire

Découvrez comment utiliser Chrome et les outils pour les développeurs afin de détecter les problèmes de mémoire qui affectent les performances des pages, y compris les fuites de mémoire, l'encombrement de la mémoire et les récupérations de mémoire fréquentes.

Résumé

  • Découvrez la quantité de mémoire utilisée par votre page avec le gestionnaire de tâches Chrome.
  • Visualisez l'utilisation de la mémoire au fil du temps avec les enregistrements de la chronologie.
  • Identifiez les arborescences DOM dissociées (une cause courante de fuites de mémoire) à l'aide d'instantanés de tas.
  • Découvrez quand de la mémoire est allouée dans votre tas de mémoire JS grâce aux enregistrements de la chronologie d'allocation.
  • Identifiez les éléments dissociés conservés par une référence JavaScript.

Présentation

Dans l'esprit du modèle de performances RAIL, vos efforts d'optimisation des performances doivent être axés sur vos utilisateurs.

Les problèmes de mémoire sont importants, car ils sont souvent perceptibles par les utilisateurs. Les utilisateurs peuvent percevoir des problèmes de mémoire de différentes manières:

  • Les performances d'une page se dégradent progressivement au fil du temps. Il s'agit peut-être d'un symptôme de fuite de mémoire. Une fuite de mémoire se produit lorsqu'un bug sur la page entraîne une utilisation progressive de plus en plus de mémoire au fil du temps.
  • Les performances d'une page sont constamment mauvaises. Il s'agit peut-être d'un symptôme de gonflement de la mémoire. Une page utilise plus de mémoire qu'il n'est nécessaire pour une vitesse de chargement optimale.
  • Les performances d'une page sont retardées ou semblent s'interrompre fréquemment. Il s'agit peut-être d'un symptôme de récupération de mémoire fréquente. La récupération de mémoire est le moment où le navigateur récupère de la mémoire. C'est le navigateur qui décide de ce moment. Pendant les collections, toute exécution de script est mise en veille. Par conséquent, si le navigateur effectue beaucoup de collecte des déchets, l'exécution du script sera souvent suspendue.

Établir la limite de la surcharge de mémoire

Une fuite de mémoire est facile à définir. Si un site utilise progressivement de plus en plus de mémoire, il y a une fuite. En revanche, l'encombrement de la mémoire est un peu plus difficile à identifier. Qu'est-ce qui est considéré comme une "utilisation excessive de mémoire" ?

Il n'existe pas de chiffres précis, car les appareils et les navigateurs ont des fonctionnalités différentes. La même page qui s'exécute sans problème sur un smartphone haut de gamme peut planter sur un smartphone d'entrée de gamme.

L'essentiel est d'utiliser le modèle RAIL et de vous concentrer sur vos utilisateurs. Identifiez les appareils populaires auprès de vos utilisateurs, puis testez votre page sur ces appareils. Si l'expérience est constamment mauvaise, la page peut dépasser les capacités de mémoire de ces appareils.

Surveiller l'utilisation de la mémoire en temps réel avec le gestionnaire de tâches Chrome

Utilisez le gestionnaire de tâches de Chrome comme point de départ pour examiner le problème de mémoire. Le gestionnaire de tâches est un moniteur en temps réel qui vous indique la quantité de mémoire utilisée par une page.

  1. Appuyez sur Maj+Échap ou accédez au menu principal de Chrome, puis sélectionnez Plus d'outils > Gestionnaire de tâches pour ouvrir le gestionnaire de tâches.

    Ouvrir le gestionnaire de tâches

  2. Effectuez un clic droit sur l'en-tête du tableau du gestionnaire des tâches, puis activez Mémoire JavaScript.

    Activation de la mémoire JavaScript dans l'en-tête du gestionnaire de tâches.

Ces deux colonnes vous indiquent différentes choses sur la façon dont votre page utilise la mémoire:

  • La colonne Espace mémoire utilisé représente la mémoire de l'OS. Les nœuds DOM sont stockés dans la mémoire de l'OS. Si cette valeur augmente, des nœuds DOM sont créés.
  • La colonne Mémoire JavaScript représente le tas de mémoire JavaScript. Cette colonne contient deux valeurs. La valeur qui vous intéresse est le nombre en direct (celui entre parenthèses). Le nombre en temps réel représente la quantité de mémoire utilisée par les objets accessibles sur votre page. Si ce nombre augmente, cela signifie que de nouveaux objets sont créés ou que les objets existants augmentent.

    Gestionnaire de tâches avec l'en-tête de mémoire JavaScript activé.

Visualiser les fuites de mémoire avec les enregistrements de performances

Vous pouvez également utiliser le panneau Performances comme point de départ de votre investigation. Le panneau "Performances" vous aide à visualiser l'utilisation de la mémoire d'une page au fil du temps.

  1. Ouvrez le panneau Performances dans les outils pour les développeurs.
  2. Cochez la case Mémoire.
  3. Créez un enregistrement.

Pour illustrer les enregistrements de mémoire Performance, considérez le code suivant:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Chaque fois que le bouton référencé dans le code est enfoncé, dix mille nœuds div sont ajoutés au corps du document, et une chaîne d'un million de caractères x est transmise au tableau x. L'exécution de ce code produit un enregistrement de la chronologie semblable à la capture d'écran suivante:

Exemple simple de croissance.

Tout d'abord, une explication de l'interface utilisateur. Le graphique HEAP dans le volet Overview (Aperçu) (sous NET) représente la pile JS. Sous le volet Vue d'ensemble se trouve le volet Compteur. Vous pouvez y voir l'utilisation de la mémoire ventilée par tas de mémoire JavaScript (identique au graphique HEAP dans le volet Vue d'ensemble), les documents, les nœuds DOM, les écouteurs et la mémoire du GPU. Si vous désactivez une case à cocher, elle ne s'affiche plus dans le graphique.

Analyse du code par rapport à la capture d'écran. Si vous examinez le compteur de nœuds (le graphique vert), vous pouvez voir qu'il correspond parfaitement au code. Le nombre de nœuds augmente par étapes distinctes. Vous pouvez supposer que chaque augmentation du nombre de nœuds correspond à un appel à grow(). Le graphique de la pile JS (graphique bleu) n'est pas aussi simple. Conformément aux bonnes pratiques, la première baisse est en réalité une récupération de mémoire forcée (réalisée en appuyant sur le bouton Collecter les déchets). À mesure que l'enregistrement progresse, vous pouvez voir que la taille du tas JS augmente. Cela est naturel et attendu: le code JavaScript crée les nœuds DOM à chaque clic de bouton et effectue beaucoup de travail lorsqu'il crée la chaîne d'un million de caractères. L'essentiel ici est que la pile JS se termine plus haut qu'elle ne commence (le "début" étant ici le point après la collecte des déchets forcée). Dans le monde réel, si vous observiez ce schéma d'augmentation de la taille de la pile JS ou de la taille du nœud, cela pourrait signifier une fuite de mémoire.

Détecter les fuites de mémoire de l'arborescence DOM dissociée à l'aide d'instantanés de tas de mémoire

Un nœud DOM ne peut être collecté que lorsqu'il n'est pas référencé par l'arborescence DOM de la page ni par le code JavaScript. Un nœud est dit "détaché" lorsqu'il est supprimé de l'arborescence DOM, mais que certains éléments JavaScript y font toujours référence. Les nœuds DOM dissociés sont une cause courante de fuites de mémoire. Cette section explique comment utiliser les profileurs de tas de DevTools pour identifier les nœuds dissociés.

Voici un exemple simple de nœuds DOM dissociés.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

Cliquer sur le bouton référencé dans le code crée un nœud ul avec dix enfants li. Ces nœuds sont référencés par le code, mais n'existent pas dans l'arborescence DOM. Ils sont donc dissociés.

Les instantanés de tas sont un moyen d'identifier les nœuds dissociés. Comme leur nom l'indique, les instantanés de tas de mémoire vous montrent comment la mémoire est répartie entre les objets JavaScript et les nœuds DOM de votre page au moment de l'instantané.

Pour créer un instantané, ouvrez DevTools, accédez au panneau Mémoire, sélectionnez la case d'option Instantané de tas, puis appuyez sur le bouton Prendre un instantané.

Case d&#39;option &quot;Prendre un instantané de tas&quot; sélectionnée.

Le traitement et le chargement de l'instantané peuvent prendre un certain temps. Une fois l'opération terminée, sélectionnez-la dans le panneau de gauche (nommé Snapshots de tas).

Saisissez Detached dans la zone de saisie Filtre de classe pour rechercher des arbres DOM dissociés.

Filtrage des nœuds dissociés.

Développez les carats pour examiner un arbre dissocié.

Examen d&#39;un arbre détaché.

Cliquez sur un nœud pour l'examiner plus en détail. Dans le volet Objects (Objets), vous pouvez obtenir plus d'informations sur le code qui le référence. Par exemple, dans la capture d'écran suivante, vous pouvez voir que la variable detachedTree fait référence au nœud. Pour corriger cette fuite de mémoire particulière, vous devez étudier le code qui utilise detachedTree et vous assurer qu'il supprime sa référence au nœud lorsqu'il n'est plus nécessaire.

Étude d&#39;un nœud dissocié.

Identifier les fuites de mémoire du tas de mémoire JS avec les chronologies d'allocation

La chronologie des allocations est un autre outil qui peut vous aider à détecter les fuites de mémoire dans votre tas JS.

Pour illustrer la chronologie d'allocation, considérez le code suivant:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Chaque fois que le bouton référencé dans le code est activé, une chaîne d'un million de caractères est ajoutée au tableau x.

Pour enregistrer une chronologie des allocations, ouvrez DevTools, accédez au panneau Mémoire, sélectionnez la case d'option Allocations sur la chronologie, appuyez sur le bouton Enregistrer, effectuez l'action qui, selon vous, est à l'origine de la fuite de mémoire, puis appuyez sur le bouton Arrêter l'enregistrement lorsque vous avez terminé.

Pendant l'enregistrement, vérifiez si des barres bleues s'affichent dans la chronologie d'allocation, comme dans la capture d'écran suivante.

Nouvelles allocations dans la chronologie des performances.

Ces barres bleues représentent les nouvelles allocations de mémoire. Ces nouvelles allocations de mémoire sont des candidats pour les fuites de mémoire. Vous pouvez faire un zoom sur une barre pour filtrer le volet Constructeur afin de n'afficher que les objets qui ont été alloués pendant la période spécifiée.

Une chronologie d&#39;allocation agrandie.

Développez l'objet et cliquez sur sa valeur pour afficher plus de détails dans le volet Object (Objet). Par exemple, dans la capture d'écran ci-dessous, en consultant les détails de l'objet nouvellement alloué, vous pouvez voir qu'il a été alloué à la variable x dans le champ d'application Window.

Détails de l&#39;objet d&#39;un tableau de chaînes.

Examiner l'allocation de mémoire par fonction

Utilisez le type de profil Échantillonnage d'allocation dans le panneau Mémoire pour afficher l'allocation de mémoire par fonction JavaScript.

Profileur d&#39;échantillonnage des allocations dans le panneau &quot;Mémoire&quot;.

  1. Sélectionnez la case d'option Échantillonnage d'allocation. Si un worker se trouve sur la page, vous pouvez le sélectionner comme cible de profilage dans la fenêtre Sélectionner l'instance de VM JavaScript.
  2. Appuyez sur le bouton Start (Démarrer).
  3. Effectuez les actions sur la page que vous souhaitez examiner.
  4. Appuyez sur le bouton Arrêter lorsque vous avez terminé.

DevTools affiche une répartition de l'allocation de mémoire par fonction. La vue par défaut est Heavy (Bottom Up) (Intensif (de bas en haut)), qui affiche les fonctions qui ont alloué le plus de mémoire en haut.

Page des résultats du profil d&#39;allocation.

Identifier les objets conservés par une référence JS

Le profil Éléments dissociés affiche les éléments dissociés qui persistent, car ils sont référencés par du code JavaScript.

Enregistrez un profil Éléments détachés pour afficher le nombre exact de nœuds HTML et les nœuds.

Exemple de profil d&#39;éléments dissociés.

Détecter les récupérations de mémoire fréquentes

Si votre page semble se mettre en pause fréquemment, vous rencontrez peut-être des problèmes de récupération des déchets.

Vous pouvez utiliser le gestionnaire de tâches de Chrome ou les enregistrements de mémoire de Vos trajets pour repérer les collectes de déchets fréquentes. Dans le gestionnaire de tâches, les valeurs Mémoire ou Mémoire JavaScript qui augmentent et diminuent fréquemment représentent des récupérations de mémoire fréquentes. Dans les enregistrements de la chronologie, les graphiques de tas de mémoire JS ou de nombre de nœuds qui augmentent et diminuent fréquemment indiquent des collectes de déchets fréquentes.

Une fois que vous avez identifié le problème, vous pouvez utiliser un enregistrement de la chronologie d'allocation pour savoir où la mémoire est allouée et quelles fonctions sont à l'origine des allocations.