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 segments de mémoire sous Mémoire > Profils > Instantané de tas de mémoire et identifiez 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 des fuites de mémoire. Pour en savoir plus, consultez la section Conservation d'arborescence d'objets.

Prendre un instantané

Pour prendre un instantané de segment de mémoire, procédez comme suit:

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

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

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

Taille totale des objets accessibles.

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

Instantané du segment de mémoire des objets "Item" dispersés.

Effacer les instantanés

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

Effacez tous les profils.

Afficher les instantanés

Pour inspecter des instantanés sous différents angles à des fins différentes, sélectionnez l'une des vues dans le menu déroulant situé en haut de l'écran:

View Contenu Objectif
Résumé Objets regroupés par nom de constructeur. Utilisez-la pour rechercher des objets et leur utilisation de mémoire en fonction de leur type. Cette fonctionnalité est utile pour suivre les fuites DOM.
Comparatif Différences entre deux instantanés. Utilisez-le pour comparer deux instantanés (ou plus) avant et après une opération. Vérifiez 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 permet d'analyser les objets référencés dans l'espace de noms global (fenêtre) pour trouver ce qui les conserve. Utilisez-la pour analyser les routes fermées et plonger dans vos objets à faible niveau.
Statistiques Graphique à secteurs de l'allocation de mémoire Affichez la taille réelle 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ésumé sélectionnée dans le menu déroulant situé en haut de l'écran.

Vue récapitulative

Initialement, un instantané de segment de mémoire s'ouvre dans la vue 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 le nom que vous souhaitez inspecter dans le filtre de classe en haut de la vue Summary (Résumé).

Les chiffres associés aux noms des constructeurs indiquent le nombre total d'objets créés avec le constructeur. La vue Récapitulatif contient également les colonnes suivantes:

  • Distance indique la distance jusqu'à la racine en utilisant le chemin simple le plus court de nœuds.
  • La taille superficielle indique la somme des tailles superficielles de tous les objets créés par un certain constructeur. La taille superficielle correspond à 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 grandes. Consultez également la section Tailles des objets.
  • Taille conservée indique la taille maximale conservée parmi le même ensemble d'objets. La taille conservée est la taille de la mémoire que vous pouvez libérer en supprimant un objet et en rendant ses dépendances inaccessibles. Consultez également la section 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 situé après 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.

Filtres du constructeur

La vue Summary (Résumé) vous permet de filtrer les constructeurs en fonction des cas courants d'utilisation inefficace de la mémoire.

Pour utiliser ces filtres, sélectionnez l'une des options suivantes dans le menu déroulant situé tout à droite de la barre d'action:

  • Tous les objets: tous les objets capturés par l'instantané actuel. Défini par défaut.
  • Objets alloués avant l'instantané 1: objets créés et conservés en mémoire avant la prise du premier instantané.
  • Objets alloués entre les instantanés 1 et les instantanés 2: visualisez la différence d'objets entre l'instantané le plus récent et l'instantané précédent. Chaque nouvel instantané ajoute un incrément de ce filtre à la liste déroulante.
  • Chaînes en double: valeurs de chaîne stockées plusieurs fois en mémoire.
  • Objets conservés par des nœuds dissociés: objets conservés en vie parce qu'un nœud DOM dissocié les référence.
  • Objets conservés par la console DevTools: objets conservés en mémoire parce qu'ils ont été évalués ou avec lesquels ils ont interagi via la console DevTools.

Entrées spéciales dans le résumé

En plus de procéder au regroupement par constructeur, la vue Summary (Résumé) regroupe les objets selon les critères suivants:

  • 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 du constructeur.

(array)

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

Par exemple, le contenu des objets JavaScript Array est stocké dans un objet interne secondaire nommé (object elements)[] pour 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 inclut 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 qu'elle puisse s'exécuter plus rapidement. Si une fonction ne s'exécute pas depuis un certain temps, V8 peut effacer les données internes de cette fonction.

(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 et activez Paramètres > Paramètres > Tests > case à cocher Afficher l'option permettant d'exposer les données internes dans les instantanés de segments de mémoire.
  2. Ouvrez le panneau Memory (Mémoire), sélectionnez radio_button_checked Instantané de tas de mémoire, puis activez check_box Exposer les composants internes (inclut des informations supplémentaires spécifiques à l'implémentation).
  3. Reproduisez le problème qui a entraîné la conservation d'une grande quantité de mémoire par InternalNode.
  4. Prendre un instantané du tas de mémoire Dans cet instantané, les objets portent des noms de classe C++ au lieu de InternalNode.
(object shape)

Comme décrit dans Propriétés rapides dans V8, V8 suit les classes cachées (ou formes) afin que plusieurs objets ayant les mêmes propriétés 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) et les données associées.

(sliced string)

Lorsque V8 doit prendre une sous-chaîne, par exemple lorsque le code JavaScript appelle String.prototype.substring(), V8 peut choisir d'allouer un objet de chaîne segmentée plutôt que 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 une grande quantité de mémoire, il est possible que le programme ait déclenché le problème 2869 et qu'il soit utile 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, c'est-à-dire un champ d'application JavaScript auquel une fonction imbriquée peut accéder.

Chaque instance de fonction contient un pointeur interne vers la Context dans laquelle 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

La vue Comparison (Comparaison) vous permet de trouver les objets divulgués en comparant plusieurs instantanés entre eux. Par exemple, le fait d'effectuer une action et de l'inverser, comme ouvrir un document et le fermer, ne doit pas laisser d'objets supplémentaires derrière.

Pour vérifier qu'une opération donnée ne crée pas de fuites:

  1. Prenez un instantané de segment de mémoire avant d'effectuer une opération.
  2. Effectuez une opération. En d'autres termes, interagissez avec une page d'une manière qui, selon vous, peut être à l'origine d'une fuite.
  3. Effectuez une opération inverse. Autrement dit, faites l'interaction opposée et répétez l'opération plusieurs fois.
  4. Prenez un deuxième instantané de segment de mémoire et passez à l'affichage Comparison (Comparaison) pour le comparer à l'Instantané 1.

La vue Comparison (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 l'isolement

La vue Confinement est une vue plongeante de la structure des objets de votre application. Il vous permet d'examiner les fermetures de fonctions, d'observer les objets internes de VM qui forment ensemble vos objets JavaScript et de comprendre la quantité de mémoire utilisée par votre application à très bas niveau.

La 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 constituées de mappages d'objets intégrés, de tables de symboles, de piles de threads de VM, de caches de compilation, de champs d'application de handle et de descripteurs globaux.
  • Objets natifs : Objets de navigateur "transmis" dans la machine virtuelle JavaScript pour permettre l'automatisation, comme les nœuds DOM et les règles CSS.

Vue Confinement.

Section "Personnes ayant conservé des données"

La section Éléments de conservation en bas du panneau Mémoire affiche les objets qui pointent vers l'objet sélectionné dans la vue. Le panneau Memory (Mémoire) met à jour la section Keepers (Éléments de conservation) lorsque vous sélectionnez des objets différents dans l'une des vues, à l'exception de Statistics (Statistiques).

Section Personnes de conservation

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

Ignorer les éléments de conservation

Vous pouvez masquer les éléments de conservation pour voir s'il y a d'autres objets qui conservent l'élément sélectionné. Avec cette option, vous n'avez pas besoin de supprimer d'abord cet élément de conservation du code, puis de reprendre l'instantané du tas de mémoire.

Option "Ignorer cet élément de conservation" dans le menu déroulant.

Pour masquer un élément de conservation, effectuez un clic droit et sélectionnez Ignorer cet élément de conservation. Les éléments de conservation ignorés sont marqués comme ignored dans la colonne Distance. Pour arrêter d'ignorer tous les éléments de conservation, cliquez sur playlist_remove Restaurer les éléments de conservation ignorés dans la barre d'action en haut de la page.

Trouver 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 et saisir l'ID de l'objet.

Nommer les fonctions pour distinguer les routes fermées

Il est très utile de nommer les fonctions afin de pouvoir faire la distinction entre les routes fermées 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.

Identifier 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 d'identifier des fuites autrement invisibles qui se produisent en raison de sous-arborescences DOM dissociées oubliées qui flottent.

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

  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 jusqu'à #tree de manière récursive. Ainsi, ce n'est que lorsque la valeur de leafRef est nulle que l'arborescence entière sous #tree est candidate à la récupération de mémoire.

Sous-arborescences DOM