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 tas de mémoire affiche la répartition de la mémoire entre les objets JavaScript et les nœuds DOM associés de votre page. Utilisez-le pour prendre des instantanés de la mémoire tampon JS, 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 tas de mémoire :
- Sur une page que vous souhaitez profiler, ouvrez les outils de développement, puis accédez au panneau Memory (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 réalisation 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 les instantanés sous différents angles et à des fins différentes, sélectionnez l'une des vues dans le menu déroulant 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. Cette fonctionnalité est 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 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 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 nombres à 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 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 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 conservée maximale 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 peu profondes 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 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 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 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 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 "Récapitulatif"
En plus de regrouper les objets par constructeurs, la vue Résumé les regroupe également par :
- Des fonctions intégrées telles que
Array
ouObject
- Éléments HTML regroupés par balises, par exemple
<div>
,<a>
,<img>
et autres. - 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 inclut divers objets internes ressemblant à 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)[]
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 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, tels que les objets C++ définis par Blink.
Pour afficher les noms de classe C++, utilisez Chrome pour le test et procédez comme suit :
- Ouvrez les outils de développement et activez > Paramètres > Tests > Afficher l'option permettant d'exposer les données internes dans les instantanés de segment de mémoire.
- 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 rétention de beaucoup 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 lien avec JavaScript Map
) et les données associées.
(sliced string)
Lorsque V8 doit extraire une sous-chaîne, par exemple lorsque le code JavaScript appelle String.prototype.substring()
, V8 peut choisir d'allouer un objet 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 beaucoup 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 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 à 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 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 totale, les instances d'objets ajoutées et supprimées sont affichées:
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 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 identifiants globaux.
- 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.
Trouver 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 des fonctions pour distinguer les fermetures
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;
}
Cet exemple effectue les opérations suivantes :
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 tas 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 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.