Découvrez comment enregistrer des instantanés de tas de mémoire avec Mémoire > Profils > Instantané de tas de mémoire et 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 JavaScript, analyser des graphiques de mémoire, comparer des instantanés et détecter des fuites de mémoire. Pour en savoir plus, consultez Arbre de conservation des objets.
Prendre un instantané
Pour prendre un instantané de segment de mémoire, procédez comme suit:
- Sur une page que vous souhaitez profiler, ouvrez les outils pour les développeurs, puis accédez au panneau Mémoire.
- Sélectionnez le type de profilage Instantané de tas , puis sélectionnez une instance de VM JavaScript, puis cliquez sur Prendre un instantané.
Lorsque le panneau Mémoire charge et analyse l'instantané, il affiche la taille totale des objets JavaScript accessibles sous le titre de l'instantané dans la section INSTANTANÉS DE SEGMENT DE MÉMOIRE.
Les instantanés n'affichent que les objets du graphique de mémoire accessibles depuis l'objet global. La création d'un instantané commence toujours par la récupération de mémoire.
Effacer les instantanés
Pour supprimer tous les instantanés, cliquez sur
Effacer 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:
Afficher | Contenu | Objectif |
---|---|---|
Résumé | Objets regroupés par nom de constructeur. | Utilisez-le pour rechercher des objets et leur utilisation de la mémoire en fonction du type. Utile pour suivre les fuites DOM. |
Comparatif | Différences entre deux instantanés. | Utilisez-le pour comparer deux (ou plusieurs) instantanés, 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 de la mémoire libérée et du 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 déterminer ce qui les maintient. Utilisez-le pour analyser les fermetures et examiner vos objets à un niveau bas. |
Statistiques | Graphique à secteurs de l'allocation de mémoire | Consultez les tailles relatives des parties de mémoire allouées au code, aux chaînes, aux tableaux JavaScript, aux tableaux typés et aux objets système. |
Affichage récapitulatif
Initialement, un instantané de tas s'ouvre dans la vue Récapitulatif, qui liste les constructeurs dans une colonne. Vous pouvez développer les constructeurs pour afficher les objets qu'ils ont instanciés.
Pour filtrer les constructeurs non pertinents, saisissez le nom que vous souhaitez inspecter dans le filtre de classe en haut de la vue Récapitulatif.
Les chiffres à côté des noms des constructeurs indiquent le nombre total d'objets créés avec le constructeur. La vue Récapitulatif affiche également les colonnes suivantes :
- Distance indique la distance par rapport à la racine en utilisant le chemin simple le plus court entre les nœuds.
- Taille peu profonde indique la somme des tailles peu profondes de tous les objets créés par un certain constructeur. La taille peu profonde correspond à la taille de la mémoire détenue par un objet lui-même. En général, les tableaux et les chaînes ont des tailles peu profondes plus importantes. 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 correspond à la taille de 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 Récapitulatif 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'ID unique de l'objet. Il vous permet de comparer les instantanés de tas de mémoire par objet.
Filtres de constructeur
La vue Récapitulatif 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 le plus à 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 restés en mémoire avant la prise du premier instantané.
- Objets alloués entre les instantanés 1 et 2: affichez la différence entre les objets de l'instantané le plus récent et ceux de 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 qui ont été stockées plusieurs fois en mémoire.
- Objets conservés par des nœuds dissociés : objets conservés, car un nœud DOM dissocié les référence.
- Objets conservés par la console des outils de développement: objets conservés en mémoire, car ils ont été évalués ou avec lesquels vous avez interagi via la console des outils de développement.
Entrées spéciales dans "Récapitulatif"
En plus de regrouper les objets par constructeurs, la vue Résumé les regroupe également par :
- Fonctions intégrées telles que
Array
ouObject
. - Éléments HTML regroupés en fonction de leurs balises (par exemple,
<div>
,<a>
,<img>
, etc.). - Fonctions que vous avez définies dans votre code.
- Catégories spéciales qui ne sont pas basées sur des constructeurs.
(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)[]
, ce qui permet de redimensionner plus facilement. 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 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 de cette fonction.
(concatenated string)
Lorsque V8 concatène deux chaînes, comme 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 ne s'agit que 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, comme les objets C++ définis par Blink.
Pour afficher les noms des classes C++, utilisez Chrome for Testing et procédez comme suit:
- Ouvrez DevTools, puis activez Settings > Experiments > Show option to expose internals in heap snapshots (Paramètres > Tests > Afficher l'option permettant d'exposer les éléments internes dans les instantanés de tas).
- Ouvrez le panneau Mémoire, sélectionnez Snapshot de tas, puis activez la Exposer les éléments internes (y compris des détails supplémentaires propres à l'implémentation).
- Reproduisez le problème qui a entraîné la conservation d'une grande quantité de mémoire par
InternalNode
. - Prenez un instantané de 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 masqué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 Map
JavaScript) 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 ne s'agit que 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, le programme peut avoir déclenché le problème 2869. Il peut être 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 d'une clôture, 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 qu'elle puisse accéder à ces variables. Même si les objets Context
ne sont pas directement visibles depuis JavaScript, vous pouvez les contrôler directement.
(system)
Cette catégorie contient divers objets internes qui n'ont (pas encore) été catégorisés de manière plus pertinente.
Comparaison
La vue Comparaison vous permet de trouver des objets divulgués en comparant plusieurs instantanés. 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 :
- Prenez un instantané de tas avant d'effectuer une opération.
- Effectuez une opération. Autrement dit, interagissez avec une page qui, selon vous, pourrait causer une fuite.
- Effectuez une opération inverse. Autrement dit, faites l'interaction opposée et répétez l'opération plusieurs fois.
- Créez un deuxième instantané de tas et définissez sa vue sur Comparison (Comparaison), en le comparant à l'instantané 1.
La vue Comparaison affiche la différence entre deux instantanés. Lorsque vous développez une entrée de total, les instances d'objet ajoutées et supprimées s'affichent:
Vue de la structuration
La vue Structuration est une vue d'ensemble de la structure des objets de votre application. Il vous permet d'examiner les fermetures de fonction, d'observer les objets internes de la VM qui constituent vos objets JavaScript et de comprendre la quantité de mémoire utilisée par votre application à un niveau très bas.
La vue fournit plusieurs points d'entrée :
- Objets DOMWindow Objets globaux pour le code JavaScript.
- Racines GC Racines de récupération de mémoire utilisées par le récupérateur de mémoire de la VM Les racines GC peuvent être constituées de cartes d'objets intégrées, de tables de symboles, de piles de threads VM, de caches de compilation, de portées de poignées et de poignées globales.
- Objets natifs Objets du navigateur "poussés" dans la machine virtuelle JavaScript pour permettre l'automatisation, par exemple, les nœuds DOM et les règles CSS.
Section "Appareils de contention"
La section Retainers (Rétenteurs) en bas du panneau Memory (Mémoire) affiche les objets qui pointent vers l'objet sélectionné dans la vue. Le panneau Mémoire met à jour la section Conservateurs lorsque vous sélectionnez un autre objet dans l'une des vues, à l'exception de Statistiques.
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 repères pour savoir si d'autres objets retiennent l'objet sélectionné. Avec cette option, vous n'avez pas besoin de supprimer d'abord ce retenant du code, puis de reprendre l'instantané de la pile.
Pour masquer un appareil de retenue, effectuez un clic droit, puis sélectionnez Ignorer cet appareil de retenue. Les éléments de conservation ignorés sont marqués comme ignored
dans la colonne Distance. Pour ne plus ignorer tous les retiens, cliquez sur Restore ignored retainers (Rétablir les retiens ignorés) dans la barre d'action en haut de l'écran.
Rechercher un objet spécifique
Pour trouver un objet dans le tas de mémoire collecté, vous pouvez effectuer une recherche à l'aide de 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 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;
}
Détecter 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 invisibles qui se produisent en raison de sous-arbres DOM détachés oubliés.
Les fuites DOM peuvent être plus importantes que vous ne le pensez. Voici un exemple. Quand la récupération de mémoire #tree
est-elle effectué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.