Maison  >  Article  >  interface Web  >  Introduction graphique à l'analyse de la mémoire JavaScript des outils de développement Chrome

Introduction graphique à l'analyse de la mémoire JavaScript des outils de développement Chrome

黄舟
黄舟original
2017-03-14 15:28:102215parcourir

Une fuite de mémoire est une diminution progressive de la mémoire disponible de l’ordinateur. Cela se produit lorsqu'un programme ne parvient pas de manière persistante à libérer la mémoire temporaire qu'il utilise. Les applications Web JavaScript rencontrent également souvent des problèmes liés à la mémoire qui surviennent dans les applications natives, tels que des fuites et des débordements. Les applications Web doivent également gérer des pauses de récupération de place. .

Bien que JavaScript utilise le garbage collection pour la gestion automatique de la mémoire, une gestion efficace de la mémoire reste importante. Dans cet article, nous explorerons l'analyse des problèmes de mémoire dans les applications Web JavaScript. Au fur et à mesure que vous découvrez les fonctionnalités, assurez-vous d’essayer les exemples pour mieux comprendre le fonctionnement de ces outils dans la pratique. Veuillez lire la page Mémoire 101 pour vous familiariser avec la terminologie utilisée dans cet article. Remarque : Certaines des fonctionnalités que nous allons utiliser ne sont actuellement disponibles que dans la version Chrome Canary du navigateur. Nous vous recommandons d'utiliser cette version pour bénéficier des meilleurs outils d'analyse des problèmes de mémoire de votre application.

Questions auxquelles vous devez réfléchir

En général, lorsque vous pensez avoir rencontré un problème de fuite de mémoire, vous devez réfléchir à trois questions :

  • Ma page occupe-t-elle trop de mémoire ? - L'outil d'affichage de la mémoire Time (vue de la mémoire Timeline) et le gestionnaire de tâches Chrome (gestionnaire de tâches Chrome) peuvent vous aider à confirmer si vous avez utilisé plus de mémoire. . La vue mémoire peut suivre le nombre de nœuds DOM , le nombre de documents document et le nombre d'événements JS d'écoute pendant le rendu de la page. En règle générale : évitez les références aux éléments DOM dont vous n'avez plus besoin, supprimez les écouteurs d'événements inutiles et soyez prudent lorsque vous stockez de gros morceaux de données que vous ne pouvez pas utiliser.

  • Ma page présente-t-elle une fuite de mémoire ? - ObjetSuivi des allocations (Objet touslocation tracker) vous aide à localiser les fuites en visualisant l'allocation des objets JS en temps réel. Vous pouvez également utiliser l'analyseur de tas (Heap Profiler) pour générer des instantanés de tas JS et découvrir les objets qui n'ont pas été nettoyés par le garbage collection en analysant la carte mémoire et en comparant les différences entre les instantanés.

  • À quelle fréquence ma page est-elle récupérée ? - Si votre page est fréquemment collectée, cela signifie qu'elle est peut-être allouée trop fréquemment. La vue de la mémoire chronologique peut vous aider à trouver des pauses intéressantes.

Terminologie et Concepts de base

Cette section est introduite dans Analyse de la mémoire Termes courants utilisés dans les outils d'analyse de la mémoire pour autres langues. Les termes et concepts présentés ici sont utilisés dans l'outil d'interface utilisateur Heap Profiler et dans la documentation associée.

Ceux-ci peuvent nous aider à nous familiariser avec la manière d'utiliser efficacement les outils d'analyse de la mémoire. Si vous avez déjà utilisé des outils d'analyse de mémoire pour des langages comme Java, .NET, etc., alors ce sera une revue.

Tailles des objets

Considérez la mémoire comme un ensemble de types de base (comme les nombres et les Chaînes) et d'objets (Tableaux associatifs). Cela pourrait ressembler au diagramme suivant représentant une série de points liés.

Un objet a deux manières d'utiliser la mémoire :

  • L'objet lui-même utilise directement

  • Maintenir implicitement les références à d'autres objets. Cette méthode empêchera le garbage collection (GC) de recycler automatiquement ces objets.

Lorsque vous utilisez le Heap Profiler dans DevTools (Heap Profiler, un outil utilisé pour analyser les problèmes de mémoire, sous l'onglet "Profil" de DevTools), vous pourriez être surpris de trouver des colonnes qui afficher diverses informations. Deux d'entre eux sont : Occupant directement la mémoire (taille peu profonde) et Occupant la mémoire totale (taille conservée) Alors, qu'est-ce qu'ils signifient ?

Occuper directement la mémoire (Shallow Size, excluant la mémoire occupée par l'objet référencé)

C'est la mémoire occupée par l'objet lui-même.

Un objet JavaScript typique aura une mémoire réservée pour décrire l'objet et stocker sa valeur directe. Généralement, seuls les tableaux et les chaînes occuperont directement de manière significative la mémoire (taille peu profonde). Mais les chaînes et les tableaux stockent souvent la partie principale des données dans la mémoire du moteur de rendu, exposant uniquement un petit objet wrapper dans la pile d'objets JavaScript.

La mémoire du moteur de rendu fait référence à toute la mémoire utilisée lors du processus de rendu de la page que vous analysez : JS dans les processus de travail concernés (workers) déclenchés par la page mémoire utilisée par le tas JS dans la page mémoire de la page lui-même La mémoire utilisée par le tas. Cependant, un petit objet peut indirectement occuper une grande quantité de mémoire en empêchant d'autres objets d'être automatiquement collectés par le garbage collection.

Mémoire totale occupée (Taille conservée, y compris la mémoire occupée par les objets référencés)

Une fois qu'un objet est supprimé, les objets dépendants qu'il référence ne peuvent pas être GC root (GC root) y fait référence, et la mémoire occupée par eux sera libérée. La mémoire totale occupée par un objet inclut la mémoire occupée par ces objets dépendants.

GC root est composé de contrôleurs(handles) Ces contrôleurs (qu'ils soient locaux ou globaux) est créé lorsqu'une référence est établie par la fonction intégrée(code natif) à un objet JavaScript en dehors du moteur V8. Tous ces contrôleurs peuvent être utilisés dans les racines GC (racines GC) > Handle scope et racines GC >gestionnaires globaux trouvé dans . La présentation de ces contrôleurs dans cet article peut prêter à confusion sans une compréhension approfondie de l'implémentation du navigateur. Vous n'avez pas besoin de trop vous soucier des racines et des contrôleurs GC.

Il existe de nombreuses racines internes du GC qui sont sans importance pour l'utilisateur. Du point de vue de l'application, il existe les situations suivantes :

Remarque : Nous recommandons aux utilisateurs de ne pas exécuter de code dans la console ou d'activer les points d'arrêt de débogage lors de la création d'instantanés de tas. La carte mémoire

commence par une racine, qui peut être l'objet

du navigateur ou l'objet windowNode.js module . La manière dont ces objets sont récupérés de la mémoire n'est pas sous le contrôle de l'utilisateur. Global

Les objets qui ne peuvent pas être traversés par la racine GC seront recyclés en mémoire.

Remarque : Les données dans les champs mémoire directe occupée et mémoire totale occupée sont exprimées en octets.

Arbre de mémoire totale occupé par les objets

Nous avons déjà appris que le tas est une structure de réseau composée de divers objets interdépendants. Dans le monde numérique, cette structure est appelée un

graphe ou un graphe mémoire. Les graphiques sont composés de nœuds reliés par des arêtes, et ils sont tous étiquetés.

  • Nœuds ( ou Objet) Les noms de balises des nœuds sont déterminés par le constructeurLe nom de la fonction est déterminé

  • Bords Le nom de l'étiquette est l'attributnom

Plus loin dans ce document vous apprendrez à utiliser l'analyseur de tas pour générer des instantanés. À partir de l'instantané généré par l'analyseur de tas dans la figure ci-dessous, nous pouvons voir le champ de distance : il fait référence à la distance entre l'objet et la racine du GC. Si tous les objets du même type sont à la même distance, mais qu’un petit sous-ensemble est plus éloigné les uns des autres, il se peut qu’il y ait un problème que vous devrez étudier.

Dominateurs

Les dominateurs sont comme une structure arborescente car chaque objet a un dominateur. Le contrôleur d'un objet ne peut pas faire directement référence à l'objet qu'il domine, c'est-à-dire que la structure arborescente de l'objet dominé n'est pas un arbre couvrant dans le graphique.

Dans l'image ci-dessus :

  • Le nœud 1 domine le nœud 2

  • Nœud 2 domine les nœuds 3, 4 et 6

  • le nœud 3 domine le nœud 5

  • le nœud 5 domine le nœud 8

  • Le nœud 6 domine le nœud 7

Dans l'exemple ci-dessous, le nœud #3 est le joueur dominant de #10, mais #7 est également le nœud dominant dans chaque GC vers #10 apparaît dans les chemins. Ainsi, si l’objet B apparaît dans chaque chemin depuis le nœud racine jusqu’à l’objet A, alors l’objet B est l’objet dominant de l’objet A.

Introduction à la V8

Dans cette section, nous décrirons quelques concepts liés à la mémoire qui sont liés à la Machine virtuelle JavaScript V8 (V8 VM ou VM) lié. Lors de l'analyse de la mémoire, il est utile de comprendre ces concepts afin de comprendre les instantanés de tas.

Description de l'objet JavaScript

a trois types primitifs :

  • Nombres (tels que 3.14159..)

  • Booléens (vrai ou faux)

  • Type de caractère (Chaînes) (comme 'Werner Heisenberg')

Ils ne référenceront pas d'autres valeurs, ce seront uniquement des nœuds feuilles ou des nœuds terminaux. Les

Les nombres sont stockés de deux manières :

  • entiervaleur directe de 31 bits, appelée : petits entiers (petits entiers)(SMI), ou

  • objets de tas, référencés comme Valeur de tas . Les valeurs de tas sont utilisées pour stocker des données qui ne conviennent pas au stockage SMI, telles que double, ou lorsqu'une valeur doit être encadrée, comme cette valeur, puis définir le valeur d'attribut.

Les données de caractères seront stockées des deux manières suivantes :

  • Tas de VM, Ou

  • dans la mémoire de rendu externe. À ce stade, un objet wrapper est créé pour accéder à l'emplacement de stockage, tel que les ressources de script et autres contenus stockés dans le package de page Web, au lieu d'être copié directement dans le tas de la VM.

Les objets JavaScript nouvellement créés se verront allouer de la mémoire sur le tas JavaScript (ou Tas VM). Ces objets sont gérés par le garbage collector de V8 et resteront en mémoire tant qu'il y aura une forte référence à eux. Les

Les objets locaux sont tous les objets qui ne sont pas dans le tas JavaScript, contrairement aux objets du tas, ils ne seront pas récupérés par V8 pendant leur cycle de vie Le gestionnaire ne peut que le faire. envelopper les références d'objet via JavaScript.

Chaîne de connexion est un objet formé par la fusion d'une paire de chaînes et est le résultat de la fusion. Les Chaînes de connexion ne sont fusionnées que lorsque cela est nécessaire. Comme une chaîne concaténée, une sous-chaîne doit être construite.

Par exemple : si vous concaténez a et b, vous obtenez la chaîne (a, b) qui sert à représenter le résultat de la concaténation. Si vous concaténez plus tard ce résultat avec d, vous obtenez une autre chaîne concaténée ((a, b), d).

Array (Arrays) - Les tableaux sont des objets avec des touches numériques. Ils sont largement utilisés dans le moteur V8 lors du stockage de grandes quantités de données. Les objets avec des paires clé-valeur comme les dictionnaires sont implémentés à l'aide de tableaux.

Un objet JavaScript typique peut être stocké dans l'un des deux types de tableau suivants :

  • propriétés nommées et

  • Éléments numériques

S'il n'y a que quelques propriétés, elles seront stockées directement dans l'objet JavaScript lui-même.

Carte - Un objet utilisé pour décrire un type d'objet et sa structure. Par exemple, des cartes seront utilisées pour décrire la structure des objets afin d'obtenir un accès rapide aux propriétés des objets

Groupes d'objets

Chaque groupe d'objets local est composé d'un ensemble d'objets interdépendants de. Par exemple, dans un sous-arbre DOM, chaque nœud peut accéder à son élément parent, à l'élément enfant suivant et à l'élément frère suivant, qui forment un graphe d'association. Notez que les éléments natifs ne sont pas représentés dans le tas JavaScript - c'est pourquoi leur taille est nulle lors de la création de leur objet d'habillage.

Chaque objet wrapper aura une référence à l'objet local, qui est utilisé pour transmettre des opérations sur ces objets locaux. Ces objets locaux ont également des références aux objets encapsulés. Mais cela ne crée pas de boucle irrécupérable, le GC est suffisamment intelligent pour identifier les objets locaux qui n'ont plus de référence à l'objet enveloppé et les libérer. Mais si un objet wrapper n'est pas publié, il conservera tous les groupes d'objets et les objets wrapper associés.

Conditions préalables et conseils utiles

Gestionnaire des tâches Chrome

Remarque : Lorsque vous utilisez Chrome pour le profilage de la mémoire, il est préférable de configurer un environnement de test propre.

Ouvrez le gestionnaire de mémoire de Chrome, observez le champ de mémoire et effectuez les opérations associées sur une page. Vous pouvez rapidement déterminer si cette opération entraînera une occupation importante de mémoire par la page. Vous pouvez trouver Memory Manager dans le menu Chrome > Outils ou en appuyant sur Shift Esc.

Après ouverture, faites un clic droit sur l'en-tête et sélectionnez l'option Mémoire utilisée par JavasScript.

Localiser les problèmes de mémoire via DevTools Timeline

La première étape pour résoudre le problème est de pouvoir prouver que le problème existe. Cela nécessite de créer un test reproductible qui sert de mesure de base du problème. Sans procédures reproductibles, les problèmes ne peuvent pas être mesurés de manière fiable. En d’autres termes, sans base de comparaison, il n’existe aucun moyen de savoir quels changements sont à l’origine du problème.

Le panneau Chronologie est très utile pour découvrir quand il y a un problème avec le programme. Il montre les moments où votre application Web ou votre site Web se charge et interagit. Tous les événements : du chargement des ressources au décodage de JavaScript, en passant par les calculs de style, les pauses de garbage collection et les repeintures de pages. Tous sont représentés sur la chronologie.

Lors de l'analyse des problèmes de mémoire, la Vue Mémoire sur le panneau de la chronologie peut être utilisée pour observer :

    Mémoire totale utilisée – L'utilisation de la mémoire a-t-elle augmenté ?
  • Nombre de nœuds DOM
  • Nombre de documents
  • Nombre d'auditeurs d'événements enregistrés

En savoir plus sur la localisation de la mémoire lors de l'analyse de la mémoire Pour les fuites, consultez le profilage de la mémoire de Zack Grossbart avec les outils de développement Chrome

Prouvez le existence d'un problème

La première chose à faire est de découvrir ce qui, selon vous, peut être à l'origine du problème. Quelques actions en cas de fuite de mémoire. Il peut s'agir de tout événement qui se produit sur la page, y compris les survols de la souris, les clics ou d'autres interactions susceptibles d'entraîner une dégradation des performances sur la page.

Démarrez l'enregistrement dans le panneau de la chronologie (Ctrl E ou Cmd E) et effectuez l'action que vous souhaitez tester. Pour forcer le ramassage des ordures, cliquez sur l'icône de la corbeille (

) sur le panneau.

Voici un exemple de fuite de mémoire où certains points ne sont pas récupérés :

Si après quelques tests répétés, vous voyez un graphique irrégulier (ci-dessus le panneau de mémoire) indique qu'il existe de nombreux objets éphémères dans votre programme. Et si une série d'actions ne maintient pas la mémoire dans une certaine plage et que le nombre de nœuds DOM ne revient pas au nombre du début, vous pouvez suspecter une fuite de mémoire.

Une fois que vous avez déterminé qu'il y a un problème de mémoire, vous pouvez utiliser l'

analyseur de tas sur le

panneau Profils profileur) pour localiser la source du problème. Exemple : Essayez l'exemple de croissance de la mémoire, qui peut vous aider à

entraîner

analyser efficacement les problèmes de mémoire à travers la chronologie. Recycleur de mémoire

Le collecteur de mémoire

(comme celui de la V8) doit être capable de localiser quels objets sont vivants et lesquels sont Les objets considérés comme morts (déchets) sont non référençables (unrchacuncapable). Si le

Garbage Collection

(GC) ne parvient pas à collecter les objets poubelles en raison d'erreurs logiques dans l'exécution de JavaScript, ces objets poubelles ne peuvent plus être recyclés. Des situations comme celle-ci finiront par rendre votre application de plus en plus lente. Par exemple, lorsque vous écrivez du code, certaines

variables

et écouteurs d'événements ne sont plus utilisés, mais ils sont toujours référencés par certains codes. Tant que la référence existe toujours, l'objet référencé ne peut pas être correctement recyclé par GC.

Pendant l'exécution de votre application, certains objets DOM peuvent avoir été mis à jour/supprimés. N'oubliez pas de vérifier les variables qui font référence aux objets DOM et de les définirnulles. . Vérifiez les propriétés des objets qui peuvent faire référence à d'autres objets (ou à d'autres éléments DOM). Gardez un œil sur la variable cache qui peut s'agrandir.

Heap Analyzer

Prendre un instantané

Dans le panneau Profils, sélectionnez Prendre un instantané du tas et cliquez sur Démarrer ou appuyez sur Cmd E ou Ctrl E :

L'instantané est initialement enregistré dans la mémoire du processus de rendu. Ils sont importés dans DevTools à la demande et vous pouvez les voir lorsque vous cliquez sur le bouton instantané . Lorsqu'un instantané est chargé dans DevTools et affiché, le nombre sous le titre de l'instantané indique la quantité totale de mémoire occupée par les objets JavaScript accessibles.

Exemple : essayez le garbage collection dans l'exemple action pour surveiller l'utilisation de la mémoire dans le panneau Chronologie.

Effacer les instantanés

Cliquez sur l'icône du bouton Effacer tout () pour effacer tous les instantanés :

Remarque : Fermer la fenêtre DevTools ne supprime pas les instantanés collectés de la mémoire de rendu. Lorsque DevTools est rouvert, la liste d'instantanés précédente est toujours là.

Rappelez-vous ce que nous avons mentionné précédemment, vous pouvez forcer GC dans DevTools lorsque vous prenez un instantané. Lorsque nous prenons un instantané, GC est exécuté automatiquement. Cliquez sur le bouton poubelle (ramassage des ordures) () dans la chronologie pour effectuer facilement le ramassage des ordures.

Exemple : essayez des objets dispersés et analysez-les avec Heap Profiler. Vous pouvez voir la collection d’éléments (objets).

Changer de vue d'instantané

Un instantané peut changer de vue en fonction de différentes tâches. Vous pouvez parcourir la zone de sélection dans l'image :

Voici les trois vues par défaut :

  • Résumé (résumé) - Afficher les objets par constructeur classification du nom

  • Comparaison (Contraste) - Afficher la différence des objets entre deux instantanés ;

  • Containment - peut être utilisé pour détecter le contenu du tas

DominatorsLes La vue peut être ouverte dans le panneau Paramètres - affiche l'arborescence des dominateurs. Elle peut être utilisée pour trouver les points de croissance de la mémoire.

Divisez les objets par différentes couleurs

Les attributs et les valeurs d'attribut des objets ont différents types et se distinguent automatiquement par la couleur. Chaque propriété est l'une des quatre suivantes :

  • a:property - une propriété ordinaire indexée par le nom de , représentée par .( point )opérateur, ou [] (crochet) référence, telle que ["foo bar"];

  • 0:element - Passer des propriétés ordinaires avec des index numériques, référencées par [] (crochets)

  • a:context var - propriétés au sein d'une fonction, dans le contexte de la fonction ; name Quote ;

  • a:system prop - une propriété ajoutée par la VM JavaScript et inaccessible par le code JavaScript.

L'objet nommé System n'a pas de type JavaScript correspondant. Ils sont intégrés au système d'objets JavaScript VM. La V8 place la plupart des objets intégrés et des objets JS utilisateur dans le même tas. Mais ce ne sont que des objets internes du V8.

Afficher les détails

Vue récapitulative (vue récapitulative)

Ouvrez un instantané, qui est affiché par défaut en vue récapitulative, indiquant le nombre total d'objets et peut être développé pour afficher un contenu spécifique : initialement, un instantané s'ouvre dans la vue Résumé, affichantles totaux des objets, qui peuvent être développés pour afficher les instances :

Le premier niveau est constitué des lignes "globales", elles montrent :

  • Constructeur (constructeur) signifie tous les le nombre d'instances de l'objet

  • généré par ce constructeur est affiché dans la colonne Objets Count

  • La colonne

    Taille peu profonde affiche le nombre total de tailles peu profondes (occupant directement la mémoire) de l'objet généré par le constructeur correspondant

  • Taille conservée La colonne affiche la mémoire maximale occupée par l'objet correspondant

  • DistanceLa colonne affiche la distance la plus courte de l'objet à la racine GC

Après avoir développé une ligne globale, toutes les instances d'objet seront affichées. La mémoire directe occupée et la mémoire totale occupée par chaque instance sont affichées en conséquence. Le nombre après le symbole @ est l'ID unique de l'objet. Avec lui, vous pouvez comparer différents instantanés objet par objet.

Exemple : essayez cet exemple (s'ouvre dans un nouvel onglet) pour apprendre à utiliser la vue Plan.

Rappelez-vous que les objets jaunes sont référencés par JavaScript, tandis que les objets rouges sont référencés par des nœuds détachés avec une couleur de fond jaune.

Vue de comparaison

Cette vue est utilisée pour comparer différents instantanés afin de trouver les différences entre les instantanés et de trouver des objets présentant des fuites de mémoire. Pour prouver qu'une certaine opération de l'application ne provoque pas de fuite (par exemple : une paire d'opérations et d'actions d'annulation, comme ouvrir un document puis le fermer, ne provoquera pas de fuite), vous pouvez essayer les étapes suivantes :

  1. Prenez un instantané du tas avant l'opération

  2. Effectuez une opération (faites l'action qui, selon vous, provoquera la fuite ); >

  3. Annuler l'opération précédente (inverser l'opération précédente et répéter plusieurs fois)
  4. Prendre un deuxième instantané et passer à la vue de contrôle ; avec l'instantané 1.
  5. En vue comparative, les différences entre les deux instantanés seront affichées. Lorsqu'une catégorie générale est développée, les objets ajoutés et supprimés sont affichés :

Exemple : Essayez l'exemple (ouvrir dans un nouvel onglet) pour savoir comment l'utiliser. Vérifiez les vues pour localiser les fuites de mémoire.

Vue de confinement

La vue de contrôle peut être appelée une « vue plongeante » de la structure des objets de votre application. Il vous permet de regarder à l'intérieur des fonctions, tout comme vos objets JavaScript, à l'intérieur des objets VM, vous donnant une vue de très bas niveau de l'utilisation de la mémoire dans votre application.

Cette vue propose plusieurs points d'entrée :

  • Objets DOMWindow

    - Ces objets sont des objets "globaux" pour le code JavaScript

  • Racine GC
  • - la véritable racine GC du garbage collector de la VM

  • Objet natif
  • - parcourir l'objet ; est "poussé" dans la machine virtuelle JavaScript pour effectuer des opérations automatiques, telles que : les nœuds DOM, les règles CSS (les détails seront introduits dans la section suivante.)

    La figure suivante est un Vue de contrôle typique :

Exemple : Essayez l'exemple (ouvrir dans un nouvel onglet) pour voir comment utiliser une vue de contrôle pour voir l'intérieur d'une fermeture

et

Gestion des événements. Conseils pour les fermetures

Nommer vos fonctions peut être utile pour distinguer les fonctions de fermeture dans votre instantané. Par exemple : l'exemple suivant ne nomme pas la fonction :

, tandis que l'exemple suivant nomme la fonction :

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

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

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

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

  return lC;
}

exemple : Essayez cet exemple pourquoi eval est mauvais pour analyser l'impact des fermetures en mémoire. Vous pourriez également être intéressé à essayer l’exemple suivant d’allocations de tas de journalisation.

Exposer les fuites de mémoire DOM

La particularité de cet outil est qu'il affiche les références bidirectionnelles entre les objets natifs du navigateur (nœuds DOM, règles CSS) et les objets JavaScript. Cela peut vous aider à détecter des fuites de mémoire subtiles causées par l'oubli de déréférencer un nœud enfant DOM gratuit.

Les fuites de mémoire DOM peuvent être plus courantes que vous ne le pensez. Regardez l'exemple suivant : quand l'objet #tree est-il GCed ?

représente une référence à son nœud parent (parentNode) qui
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 can be #tree GC
récursivement

fait référence à #leaf, donc seulement lorsque leafRef est annulé représente toute l'arborescence sera recyclé par GC. #tree#tree

Exemple : essayez de fuir les nœuds DOM pour comprendre où les nœuds DOM perdront de la mémoire et comment les localiser. Vous pouvez également regarder cet exemple : les fuites du DOM sont plus importantes que prévu.

Consultez l'article de Gonzalo Ruiz de Villa Rechercher et déboguer les fuites de mémoire avec Chrome DevTools pour en savoir plus sur les fuites de mémoire DOM et les bases de l'analyse de la mémoire.

Les objets natifs sont plus faciles à trouver dans les vues Résumé et Confinement : des catégories leur sont dédiées :

Exemple : Essayez cet exemple (dans le nouvel onglet ) pour apprendre à séparer l'arborescence DOM.

Vue Dominateurs

La vue Dominateurs affiche l'arborescence Dominateurs du graphique de pile. La vue Dominator est similaire à la vue Confinement, mais n'a pas de noms de propriété. En effet, la règle peut être un objet qui n'a pas de référence directe, ce qui signifie que l'arborescence des règles n'est pas un arbre couvrant du graphe de tas. Mais c’est une vue utile qui peut nous aider à localiser rapidement les points de croissance de la mémoire.

Remarque : dans Chrome Canary, la vue Dominator peut être activée dans Paramètres > Afficher les propriétés avancées de l'instantané de tas dans DevTools et prendra effet après le redémarrage de DevTools.

Exemple : essayez cet exemple (ouvert dans un nouvel onglet) pour vous entraîner à trouver des points de croissance de la mémoire. Vous pouvez essayer davantage l'exemple suivant de conservation des chemins et des dominateurs.

Object Allocation Tracker

Object Tracker intègre l'analyse de mise à jour incrémentielle d'instantané du profileur de tas et l'enregistrement du panneau Timeline. Comme d'autres outils, l'enregistrement de la configuration du tas d'un objet nécessite de démarrer l'enregistrement, d'effectuer une série d'opérations, puis d'arrêter l'enregistrement et de l'analyser.

Le suivi d'objets enregistre des instantanés de tas en continu (jusqu'à toutes les 50 millisecondes !) et enregistre le dernier instantané à la fin. Le profileur d'allocation de tas indique où un objet a été créé et son chemin réservé.

Ouvrez et utilisez Object Analyzer

Pour commencer à utiliser Object Analyzer : 1. Assurez-vous que vous utilisez la dernière version de Chrome. Canari.

  1. Ouvrez DeveTools et cliquez sur l'icône d'engrenage (Traducteur : je ne comprends pas l'utilité de cette étape).

  2. Maintenant, ouvrez le panneau Profiler et vous verrez l'option "Enregistrer les allocations de tas".

Les barres ci-dessus représentent les nouveaux objets générés dans le tas. La hauteur correspond à la taille de l'objet correspondant, et sa couleur indique si l'objet est toujours là dans le dernier instantané pris : la colonne bleue indique que l'objet est toujours là à la fin de la timeline, et la colonne grise indique que l'objet a été généré dans la chronologie, mais la mémoire a été récupérée avant la fin.

Dans l'exemple ci-dessus, une action est exécutée 10 fois. Le même programme conserve 5 objets, donc les 5 dernières barres bleues sont conservées. Mais il existe des problèmes potentiels avec la dernière colonne restante. Vous pouvez utiliser le curseur sur la chronologie pour zoomer sur cet instantané spécifique et trouver l'objet alloué.

Cliquer sur un objet dans le tas affichera son arborescence de mémoire totale réservée dans la partie inférieure de l'instantané du tas. L'examen de l'arborescence de mémoire totale réservée pour cet objet peut vous donner suffisamment d'informations pour comprendre pourquoi cet objet n'a pas été collecté, et vous pouvez ensuite apporter les modifications appropriées à votre code pour supprimer les références inutiles.

FAQ sur l'analyse de la mémoire

Q : Je ne peux pas voir toutes les propriétés d'un objet, je vois également leurs valeurs non-chaînes ! Pourquoi?

Toutes les propriétés ne sont pas stockées intactes dans le tas JavaScript. Certains d’entre eux sont obtenus en exécutant des méthodes getters de code natif. Ces propriétés ne sont pas capturées dans l'instantané du tas afin d'empêcher les appels aux getters et d'éviter les modifications de l'état du programme si ces getters ne sont pas des fonctions "pures (Pure)". De même, les valeurs autres que des chaînes, telles que les nombres, ne sont pas capturées afin de réduire la taille de l'instantané.

Q : Que signifie le numéro après le symbole @ – est-ce une adresse ou une pièce d'identité ? Cette valeur d'identification est-elle vraiment unique ?

Voici l'ID de l'objet. Afficher l'adresse d'un objet n'a aucun sens car un objet sera supprimé lors du garbage collection. Ces ID d'objet sont de véritables ID, c'est-à-dire qu'ils sont représentés de manière unique entre différents instantanés. Cela permet une comparaison précise entre les états du tas. La conservation de ces ID ajoute une surcharge supplémentaire au processus GC, mais celle-ci n'est allouée que lorsque le premier instantané de tas est enregistré - si l'analyseur de tas n'est pas utilisé, il n'y a pas de surcharge supplémentaire.

Q : Les objets « morts » (non référencés) sont-ils inclus dans les instantanés ?

Non, seuls les objets pouvant être référencés seront affichés dans l'instantané. De plus, les opérations GC seront automatiquement effectuées avant de prendre un instantané.

Remarque : Au moment de la rédaction de cet article, nous prévoyons de ne plus effectuer de GC lors de la prise d'instantanés pour éviter que la taille du tas ne soit réduite. C'est désormais le cas, mais les objets inutiles apparaissent toujours en dehors de l'instantané.

Q : En quoi consiste la racine GC ?

est composé de nombreuses parties regroupées en :

  • graphe d'objets natifs

  • Tableau des symboles ;

  • Pile dans le thread de la VM ;

  • Modifier le cache

  • Contexte du contrôleur ; ;

  • Contrôleur global.

Q : J'ai appris que je peux utiliser Heap Profiler et la vue Timeline Memory pour détecter les fuites de mémoire. Mais quel outil dois-je utiliser en premier ?

Le panneau Chronologie est utilisé pour diagnostiquer une utilisation excessive de la mémoire lorsque vous utilisez votre page pour la première fois et constatez qu'elle ralentit. Un site Web lent est un signe classique d'une fuite de mémoire, mais cela peut aussi être dû à d'autres raisons : il peut y avoir un rendu ou un goulot d'étranglement du réseau, alors assurez-vous de résoudre le véritable problème de votre page.

Pour déterminer s'il s'agit d'un problème de mémoire, ouvrez le panneau Chronologie et l'onglet Mémoire. Cliquez sur le bouton d'enregistrement et répétez l'opération sur votre application plusieurs fois si vous pensez qu'elle peut provoquer une fuite de mémoire. Arrêtez l'enregistrement. Le graphique d'utilisation de la mémoire de votre application est généré. Si l'utilisation de la mémoire continue d'augmenter (sans diminution correspondante), c'est le signe que votre application peut avoir une fuite de mémoire.

Généralement, le graphique d'utilisation de la mémoire d'une application normale est irrégulier, car la mémoire sera récupérée par le ramasse-miettes après son utilisation. Ne vous inquiétez pas de ce zigzag – il y a toujours une consommation de mémoire due à JavaScript, et même un

vide provoquera ce zigzag, ce qui est inévitable. Tant qu'il ne s'agit pas d'une forme qui alloue beaucoup de mémoire continue, cela signifie que beaucoup de déchets de mémoire sont générés. requestAnimationFrame

La ligne de croissance sur la photo ci-dessus vous oblige à vous méfier. Le compteur de nœuds DOM, le compteur de documents et le nombre d'écouteurs d'événements dans la balise Mémoire sont également très utiles lors de l'analyse de diagnostic. Le nombre de nœuds DOM correspond à la mémoire native utilisée et n'affecte pas la carte mémoire JavaScript.

Une fois que vous confirmez que votre application présente une fuite de mémoire, un analyseur de tas peut être utilisé pour trouver la fuite de mémoire.

Q : J'ai découvert que les numéros de certains nœuds DOM dans l'instantané du tas sont marqués en rouge comme « Arbre DOM détaché », tandis que d'autres sont en jaune. Qu'est-ce que cela signifie ?

Vous constaterez qu'il existe différentes couleurs. Les nœuds rouges (avec un fond sombre) n'ont aucune référence directe à eux depuis JavaScript, mais ils font partie d'une structure DOM distincte, ils restent donc en mémoire. Il est possible qu'un nœud soit référencé par JavaScript (peut-être dans une fermeture ou une variable), et cette référence empêche la récupération de l'intégralité de l'arborescence DOM.

Les nœuds jaunes (fond jaune) ont des références directes à JavaScript. Recherchez un nœud jaune dans la même arborescence DOM détachée pour localiser vos références JavaScript. Il est possible de voir la chaîne de référence d'attribut depuis la fenêtre DOM vers ce nœud (comme :

). window.foo.bar[2].baz

Le diagramme dynamique suivant montre le processus des nœuds détachés :

Exemple : essayez cet exemple de nœuds détachés et vous pourrez voir le Cycle de vie des nœuds dans Timeline, puis prenez un instantané du tas pour trouver les nœuds séparés.

Q : Que représentent respectivement l'occupation directe de la mémoire (Shallow Size) et l'occupation totale de la mémoire (Retained Size), et quelle est la différence entre elles ?

C'est le cas, les objets peuvent exister en mémoire de deux manières (être vivants) - directement retenus par un autre objet accessible (vivant) (les objets fenêtre et document sont toujours accessibles) ou des références implicitement contenues par les objets natifs (comme les objets DOM). Cette dernière méthode peut provoquer des fuites de mémoire en empêchant le recyclage automatique des objets par GC. La mémoire occupée par l'objet lui-même est appelée mémoire directe (d'une manière générale, les tableaux et les chaînes réserveront plus de mémoire directe (taille peu profonde)).

Un objet de n'importe quelle taille peut préserver une utilisation importante de la mémoire en empêchant la récupération d'autres objets. Lorsqu'un objet est supprimé (certaines dépendances qu'il crée ne peuvent plus être référencées), la quantité de mémoire pouvant être libérée est appelée mémoire totale occupée (taille conservée).

Q : Il y a beaucoup de données sous le constructeur et des champs conservés. Par où dois-je commencer pour vérifier si je rencontre une fuite de mémoire ?

De manière générale, il est préférable de commencer par le premier objet trié par retenues. Les retenues sont triées par distance (en référence à la distance par rapport à l'objet fenêtre).

L'objet avec la distance la plus courte peut être l'objet préféré, ce qui peut provoquer une fuite de mémoire.

Q : Quelles sont les différences entre les vues Résumé, Comparaison, Dominateurs et Confinement ?

Vous pouvez découvrir la différence en changeant de vue.

  • La vue Résumé vous aide à trouver des objets (et leur utilisation de la mémoire) regroupés par constructeurs. Cette vue est utile pour rechercher les fuites de mémoire DOM.

  • La vue Comparaison peut rechercher des fuites de mémoire en montrant quels objets la mémoire a été correctement récupérée. En règle générale, deux (ou plus) instantanés d'utilisation de la mémoire sont enregistrés avant et après une opération. Il vérifie s'il y a une fuite de mémoire en examinant la différence entre la mémoire libérée et le nombre de références, et en trouve la cause.

  • La vue Confinement (contrôle) offre un meilleur affichage de la structure des objets, nous aidant à analyser les références d'objets dans la portée globale (comme la fenêtre) pour découvrir ce qui retient ces objets. Il vous permet d'analyser les fermetures et d'approfondir les objets.

  • La vue Dominators peut être utilisée pour nous aider à confirmer qu'aucun objet redondant n'est toujours suspendu à un certain emplacement (comme ceux qui sont référencés), et pour confirmer la suppression/la corbeille de objets Le recyclage fait vraiment la différence.

Q : Que représente le contenu du constructeur (un groupe) dans l'analyseur de tas ?

  • (propriété globale) - entre un objet global (comme 'window') et les objets qui y font référence objet intermédiaire. Si un objet est généré par le constructeur Person et est référencé par l'objet global, le chemin de référence ressemble à ceci : [global] > (propriété globale) > Ceci est différent des objets qui se référencent directement les uns les autres. Nous utilisons des objets intermédiaires pour des raisons de performances. Les objets globaux changent fréquemment et l'optimisation de l'accès aux attributs des variables non globales ne s'applique pas aux variables globales.

  • (roots) - Le contenu des racines dans le constructeur fait référence à l'objet qu'il sélectionne. Il peut également s'agir de références créées de manière autonome par le moteur. Ce moteur dispose d'un cache pour les objets référencés, mais ces références n'empêchent pas le recyclage de l'objet référencé, ce ne sont donc pas de véritables références fortes (FIXME).

  • (fermeture) - une référence à un ensemble d'objets dans une fonction de fermeture

  • (array, string, number, regexp) - Un ensemble de propriétés faisant référence à un type d'objet de type Array, String, Number ou expression régulière

  • (code compilé) - En termes simples, tout est lié au code compilé. Le script est comme une fonction, mais correspond en réalité au contenu de 3f1c4e4b6b16bbbd69b2ee476dc4f83a. SharedFunctionInfos (SFI) sont des objets entre les fonctions et le code compilé. Les fonctions ont généralement du contenu, pas SFIS (FIXME).

  • HTMLpElement, HTMLAnchorElement, DocumentFragment, etc. – références à des éléments ou des objets de document dans votre coder.

De nombreux autres objets générés au cours du cycle de vie de votre programme, notamment des écouteurs d'événements ou des objets personnalisés, peuvent être trouvés dans les contrôleurs suivants :

Q : Dois-je désactiver les fonctionnalités de Chrome susceptibles d'avoir un impact lors de l'analyse de la mémoire ?

Nous recommandons que lorsque vous utilisez Chrome DevTools pour l'analyse de la mémoire, vous puissiez utiliser le mode navigation privée avec toutes les extensions désactivées, ou définir le dossier utilisateur sur (--user-data-dir="") avant d'ouvrir Chrome.

Les applications, extensions et même les enregistrements dans la console auront un impact potentiel sur votre analyse. Si vous souhaitez que votre analyse soit fiable, désactivez-les.

Écrivez les mots à la fin

Les moteurs JavaScript d'aujourd'hui ont déjà de solides capacités et peuvent recycler automatiquement les déchets de mémoire générés par le code. Autrement dit, ils ne peuvent aller que jusqu'à un certain point, mais il est toujours prouvé que notre application produit des fuites de mémoire dues à des erreurs logiques. Utilisez les outils appropriés pour identifier les goulots d'étranglement de votre application et n'oubliez pas de ne pas deviner : testez-la.

Exemples d'aide

Diagnostiquer une fuite de mémoire

Bien que beaucoup de choses aient été mentionnées dans cet article, une série d'exemples pour tester les problèmes liés à la mémoire sont toujours utiles, vous trouverez ci-dessous un ensemble d'exemples de fuites de mémoire de nœuds DOM. Vous souhaiterez peut-être expérimenter ces exemples avant de tester vos pages ou applications plus complexes.

  • Exemple 1 : Mémoire croissante

  • Exemple 2 : La collecte des déchets en action

  • Exemple 3 : Objets dispersés

  • Exemple 4 : Nœuds détachés

  • Exemple 5 : Mémoire et classees

  • Exemple 6 : Fuite de nœuds DOM

  • Exemple 7 : Eval est mauvais (presque toujours)

  • Exemple 8 : Enregistrement des allocations de tas

  • Exemple 9 : Fuites DOM plus importantes que prévu

  • Exemple 10 : Chemin de conservation

  • Exemple 11 : Dernier exercice

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn