Enregistrer des instantanés de segment de mémoire

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Découvrez comment enregistrer des instantanés de tas de mémoire en sélectionnant Mémoire > Profils > Instantané de tas de mémoire et comment détecter les fuites de mémoire.

Le profileur de segments de mémoire affiche la répartition de la mémoire en fonction des objets JavaScript de votre page et des nœuds DOM associés. Utilisez-le pour prendre des instantanés de segments de mémoire JS, analyser des graphiques de mémoire, comparer des instantanés et détecter les fuites de mémoire. Pour en savoir plus, consultez la section Conserver l'arborescence d'objets.

Prendre un instantané

Pour prendre un instantané de segment de mémoire:

  1. Sur la page que vous souhaitez profiler, ouvrez les Outils de développement, puis accédez au panneau Mémoire.
  2. Sélectionnez le type de profilage d'instantané de segment de mémoire radio_button_checked, puis une instance de VM JavaScript et cliquez sur Prendre un instantané.

Type de profilage et instance de VM JavaScript sélectionnés.

Lorsque le panneau Memory (Mémoire) charge et analyse l'instantané, il indique la taille totale des objets JavaScript accessibles sous le titre de l'instantané dans la section HEAP SNAPSHOTS.

Taille totale des objets accessibles.

Les instantanés n'affichent que les objets du graphique de mémoire qui sont accessibles à partir de l'objet global. La prise d'un instantané commence toujours par la récupération de mémoire.

Un instantané de segment de mémoire des objets "Items" dispersés.

Effacer les instantanés

Pour supprimer tous les instantanés, cliquez sur bloquer Effacer tous les profils:

Effacer tous les profils.

Afficher les instantanés

Pour inspecter des instantanés depuis différentes perspectives à des fins différentes, sélectionnez l'une des vues dans le menu déroulant en haut de la page:

Afficher Contenus Objectif
Résumé Objets regroupés par noms de constructeur. Utilisez-le pour rechercher des objets et leur utilisation de la mémoire en fonction de leur type. Cette méthode est utile pour le suivi des fuites DOM.
Comparaison Différences entre deux instantanés. Utilisez-le pour comparer deux instantanés (ou plus) avant et après une opération. Confirmez la présence et la cause d'une fuite de mémoire en inspectant le delta dans la mémoire libérée et le nombre de références.
Structuration Contenu du tas de mémoire Fournit une meilleure vue de la structure des objets et aide à analyser les objets référencés dans l'espace de noms global (fenêtre) pour trouver ce qui les conserve. Utilisez-le pour analyser les routes fermées et explorer vos objets à un niveau inférieur.
Statistiques Graphique à secteurs de l'allocation de mémoire Consultez les tailles réelles des parties de mémoire allouées au code, aux chaînes, aux tableaux JS, aux tableaux typés et aux objets système.

Vue récapitulative sélectionnée dans le menu déroulant en haut de l'écran.

Vue récapitulative

Au départ, un instantané du segment de mémoire s'ouvre dans la vue Summary (Résumé) qui répertorie les Constructeurs dans une colonne. Vous pouvez développer les constructeurs pour voir les objets qu'ils ont instanciés.

Vue récapitulative avec un constructeur développé

Pour filtrer les constructeurs non pertinents, saisissez un nom que vous souhaitez inspecter dans le filtre de classe en haut de la vue Summary (Résumé).

Les chiffres figurant à côté des noms de constructeur indiquent le nombre total d'objets créés avec le constructeur. La vue Récapitulatif affiche également les colonnes suivantes:

  • Distance : indique la distance jusqu'à la racine en utilisant le chemin de nœuds simple le plus court.
  • Taille superficielle : affiche la somme des tailles superficielles de tous les objets créés par un constructeur donné. La taille superficielle est la taille de la mémoire détenue par un objet lui-même. En règle générale, les tableaux et les chaînes ont des tailles superficielles plus importantes. Consultez également la page Tailles des objets.
  • Taille conservée indique la taille maximale conservée au sein d'un même ensemble d'objets. La taille conservée est la taille de mémoire que vous pouvez libérer en supprimant un objet et en rendant ses dépendances inaccessibles. Consultez également la page Tailles des objets.

Lorsque vous développez un constructeur, la vue Summary (Résumé) affiche toutes ses instances. Chaque instance reçoit une répartition de ses tailles superficielles et conservées dans les colonnes correspondantes. Le nombre qui suit le caractère @ correspond à l'identifiant unique de l'objet. Il vous permet de comparer les instantanés de tas de mémoire par objet.

Entrées spéciales dans le récapitulatif

En plus du regroupement par constructeur, la vue Summary regroupe également les objets par:

  • Des fonctions intégrées telles que Array ou Object
  • Fonctions que vous avez définies dans votre code.
  • Catégories spéciales qui ne sont pas basées sur des constructeurs.

Entrées de constructeur.

(array)

Cette catégorie comprend divers objets internes semblables à des tableaux qui ne correspondent pas directement aux objets visibles en JavaScript.

Par exemple, le contenu des objets JavaScript Array est stocké dans un objet interne secondaire nommé (object elements)[], afin de faciliter le redimensionnement. De même, les propriétés nommées dans les objets JavaScript sont souvent stockées dans des objets internes secondaires nommés (object properties)[], qui sont également répertoriés dans la catégorie (array).

(compiled code)

Cette catégorie comprend les données internes dont V8 a besoin pour exécuter des fonctions définies par JavaScript ou WebAssembly. Chaque fonction peut être représentée de différentes manières, de petite et lente à grande et rapide.

V8 gère automatiquement l'utilisation de la mémoire dans cette catégorie. Si une fonction s'exécute plusieurs fois, V8 utilise plus de mémoire pour cette fonction afin qu'elle puisse s'exécuter plus rapidement. Si une fonction n'a pas été exécutée depuis un certain temps, V8 peut effacer les données internes la concernant.

(concatenated string)

Lorsque V8 concatène deux chaînes, par exemple avec l'opérateur JavaScript +, il peut choisir de représenter le résultat en interne sous la forme d'une "chaîne concaténée", également appelée structure de données Rope.

Plutôt que de copier tous les caractères des deux chaînes sources dans une nouvelle chaîne, V8 alloue un petit objet avec des champs internes appelés first et second, qui pointent vers les deux chaînes sources. Cela permet à V8 d'économiser du temps et de la mémoire. Du point de vue du code JavaScript, il s'agit simplement de chaînes normales, qui se comportent comme n'importe quelle autre chaîne.

InternalNode

Cette catégorie représente les objets alloués en dehors de V8, tels que les objets C++ définis par Blink.

Pour afficher les noms des classes C++, utilisez Chrome for Testing et procédez comme suit:

  1. Ouvrez les outils de développement, puis activez settings Paramètres > Tests > case à cocher Afficher l'option permettant d'exposer les composants internes dans des instantanés de segments de mémoire.
  2. Ouvrez le panneau Memory (Mémoire), sélectionnez radio_button_checked Instantané de segment, puis activez la case Exposer les composants internes (inclut des informations supplémentaires spécifiques à l'implémentation).
  3. Reproduisez le problème qui a entraîné le fait que InternalNode conserve beaucoup de mémoire.
  4. Prendre un instantané de segment de mémoire Dans cet instantané, les objets ont des noms de classe C++ au lieu de InternalNode.
(object shape)

Comme décrit dans la section Propriétés rapides dans V8, V8 suit les classes (ou formes) masquées afin que plusieurs objets ayant les mêmes propriétés et dans le même ordre puissent être représentés efficacement. Cette catégorie contient ces classes masquées, appelées system / Map (sans rapport avec JavaScript Map), ainsi que les données associées.

(sliced string)

Lorsque V8 doit utiliser une sous-chaîne, par exemple lorsque le code JavaScript appelle String.prototype.substring(), il peut choisir d'allouer un objet de chaîne segmentée au lieu de copier tous les caractères pertinents de la chaîne d'origine. Ce nouvel objet contient un pointeur vers la chaîne d'origine et décrit la plage de caractères de la chaîne d'origine à utiliser.

Du point de vue du code JavaScript, il s'agit simplement de chaînes normales, qui se comportent comme n'importe quelle autre chaîne. Si une chaîne segmentée conserve beaucoup de mémoire, il est possible que le programme ait déclenché le problème 2869. Il peut être judicieux de prendre des mesures délibérées pour "aplatir" la chaîne segmentée.

system / Context

Les objets internes de type system / Context contiennent des variables locales provenant d'une fermeture, un champ d'application JavaScript auquel une fonction imbriquée peut accéder.

Chaque instance de fonction contient un pointeur interne vers le Context dans lequel elle s'exécute, afin de pouvoir accéder à ces variables. Même si les objets Context ne sont pas directement visibles à partir de JavaScript, vous pouvez les contrôler directement.

(system)

Cette catégorie contient divers objets internes qui n'ont pas (encore) été classés de manière plus significative.

Comparaison

L'affichage Comparaison vous permet de rechercher des objets divulgués en comparant plusieurs instantanés. Par exemple, vous ne devez pas laisser d'objets superflus lorsque vous effectuez une action que vous effectuez et que vous inversez (comme ouvrir et fermer un document).

Pour vérifier qu'une opération donnée ne crée pas de fuite, procédez comme suit:

  1. Prenez un instantané de segment de mémoire avant d'effectuer une opération.
  2. Effectuez une opération. c'est-à-dire interagir avec une page d'une manière qui, selon vous, pourrait être à l'origine d'une fuite.
  3. Effectuez une opération inverse. Autrement dit, faites l'interaction opposée et répétez-la plusieurs fois.
  4. Prenez un second instantané de segment de mémoire et passez à l'affichage Comparison (Comparaison) pour le comparer à l'instantané 1.

L'affichage Comparaison montre la différence entre deux instantanés. Lorsque vous développez une entrée totale, les instances d'objets ajoutées et supprimées sont affichées:

Comparaison avec l'instantané 1.

Vue de structuration

La vue Structure est une vue aérienne de la structure des objets de votre application. Il vous permet de jeter un coup d'œil à l'intérieur des fermetures de fonctions, d'observer les objets internes des VM qui constituent vos objets JavaScript, et de comprendre la quantité de mémoire utilisée par votre application à un niveau très bas.

Cette vue fournit plusieurs points d'entrée:

  • Objets DOMWindow Objets globaux pour le code JavaScript.
  • Racines de récupération de mémoire. Racines de récupération de mémoire utilisées par le récupérateur de mémoire de la VM. Les racines de récupération de mémoire peuvent être des mappages d'objets intégrés, des tables de symboles, des piles de threads de VM, des caches de compilation, des champs d'application de handle et des identifiants globaux.
  • Objets natifs. Objets du navigateur "transmis" dans la machine virtuelle JavaScript pour permettre l'automatisation, par exemple les nœuds DOM et les règles CSS.

Vue Confinement.

Section "Obligations de conservation"

La section Éléments de retenue en bas du panneau Mémoire affiche les objets qui pointent vers l'objet sélectionné dans la vue.

Section "Éléments de conservation".

Dans cet exemple, la chaîne sélectionnée est conservée par la propriété x d'une instance Item.

Rechercher un objet spécifique

Pour trouver un objet dans le tas de mémoire collecté, vous pouvez effectuer une recherche en appuyant sur Ctrl+F, puis en saisissant l'ID de l'objet.

Nommer des fonctions pour distinguer les routes fermées

Il est très utile de nommer les fonctions afin de pouvoir distinguer les fermetures dans l'instantané.

Par exemple, le code suivant n'utilise pas de fonctions nommées:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

Dans cet exemple:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

Fonction nommée dans une fermeture.

Découvrir les fuites DOM

Le profileur de segments de mémoire peut refléter les dépendances bidirectionnelles entre les objets natifs du navigateur (nœuds DOM et règles CSS) et les objets JavaScript. Cela permet de détecter les fuites autrement invisibles dues à des sous-arborescences DOM dissociées oubliées qui flottent.

Les fuites de DOM peuvent être plus importantes que vous ne le pensez. Voici un exemple. Quand #tree est-il récupéré ?

  var select = document.querySelector;
  var treeRef = select("#tree");
  var leafRef = select("#leaf");
  var body = select("body");

  body.removeChild(treeRef);

  //#tree can't be GC yet due to treeRef
  treeRef = null;

  //#tree can't be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW #tree can be garbage collected

#leaf conserve une référence à son parent (parentNode) et de manière récursive jusqu'à #tree. Ainsi, ce n'est que lorsque leafRef est nul que l'arborescence entier est éligible à la récupération de mémoire.#tree

Sous-arborescences DOM