Heap-Snapshots aufzeichnen

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Informationen zum Aufzeichnen von Heap-Snapshots mit Arbeitsspeicher > Profile > Heap-Snapshot und Speicherlecks finden

Der Heap-Profiler zeigt die Speicherverteilung auf die JavaScript-Objekte und die zugehörigen DOM-Knoten Ihrer Seite an. Damit können Sie JS-Heap-Snapshots erstellen, Arbeitsspeicherdiagramme analysieren, Snapshots vergleichen und Speicherlecks finden. Weitere Informationen finden Sie unter Objektbehalten-Baum.

Schnappschuss aufnehmen

So erstellen Sie einen Heap-Snapshot:

  1. Öffnen Sie die Entwicklertools auf einer Seite, für die Sie ein Profil erstellen möchten, und gehen Sie zum Bereich Memory.
  2. Wählen Sie den Profilerstellungstyp Heap-Snapshot aus, wählen Sie eine JavaScript-VM-Instanz aus und klicken Sie auf Snapshot erstellen.

Einen ausgewählten Profilertyp und eine JavaScript-VM-Instanz.

Wenn das Steuerfeld Arbeitsspeicher den Snapshot lädt und parst, wird die Gesamtgröße der erreichbaren JavaScript-Objekte unter dem Snapshot-Titel im Bereich HEAP-SNAPSHOTS angezeigt.

Die Gesamtgröße der erreichbaren Objekte.

In Snapshots werden nur die Objekte aus dem Speichergraphen angezeigt, die vom globalen Objekt aus erreichbar sind. Die Erstellung eines Snapshots beginnt immer mit der automatischen Speicherbereinigung.

Ein Heap-Snapshot von verstreuten „Item“-Objekten.

Snapshots löschen

Wenn Sie alle Snapshots entfernen möchten, klicken Sie auf Alle Profile löschen:

Alle Profile löschen.

Snapshots anzeigen

Wenn Sie Snapshots aus verschiedenen Perspektiven für unterschiedliche Zwecke prüfen möchten, wählen Sie oben im Drop-down-Menü eine der Ansichten aus:

Ansehen Inhalt Zweck
Zusammenfassung Objekte, die nach Konstruktornamen gruppiert sind. Damit können Sie Objekte und ihren Speicherverbrauch je nach Typ ermitteln. Nützlich für das Tracking von DOM-Leaks.
Vergleich Unterschiede zwischen zwei Snapshots. Sie können damit zwei (oder mehr) Snapshots vor und nach einem Vorgang vergleichen. Prüfen Sie das Vorhandensein und die Ursache eines Speicherlecks, indem Sie das Delta im freigegebenen Arbeitsspeicher und die Referenzanzahl untersuchen.
Begrenzung Heap-Inhalt Bietet eine bessere Ansicht der Objektstruktur und hilft bei der Analyse von Objekten, auf die im globalen Namespace (Fenster) verwiesen wird, um zu finden, wo sie vorhanden sind. Analysiere damit die Schließungen und erkunde deine Objekte auf niedriger Ebene.
Statistiken Kreisdiagramm der Arbeitsspeicherzuweisung Sehen Sie sich die tatsächlichen Größen von Speicherteilen an, die Code, Strings, JS-Arrays, typisierten Arrays und Systemobjekten zugewiesen sind.

Oben im Drop-down-Menü die Ansicht „Zusammenfassung“

Zusammenfassung

Zuerst wird in der Ansicht Zusammenfassung ein Heap-Snapshot geöffnet, in der Konstruktoren in einer Spalte aufgeführt sind. Sie können Konstruktoren erweitern, um die von ihnen instanziierten Objekte zu sehen.

Die Ansicht „Zusammenfassung“ mit einem maximierten Konstruktor.

Um irrelevante Konstruktoren herauszufiltern, geben Sie im Kursfilter oben in der Ansicht Zusammenfassung einen Namen ein, den Sie prüfen möchten.

Die Zahlen neben den Konstruktornamen geben die Gesamtzahl der Objekte an, die mit dem Konstruktor erstellt wurden. Die Ansicht Zusammenfassung enthält außerdem die folgenden Spalten:

  • Abstand gibt den Abstand zum Stammknoten über den kürzesten einfachen Pfad von Knoten an.
  • Größe ohne Unterobjekte ist die Summe der Größen ohne Unterobjekte aller Objekte, die mit einem bestimmten Konstruktor erstellt wurden. Die flache Größe ist die Größe des Arbeitsspeichers eines Objekts. Arrays und Strings haben im Allgemeinen eine größere, flache Größe. Siehe auch Objektgrößen.
  • Beibehaltene Größe zeigt die maximale beibehaltene Größe innerhalb derselben Gruppe von Objekten. Beibehaltene Größe ist die Größe des Arbeitsspeichers, den Sie freigeben können, indem Sie ein Objekt löschen und seine abhängigen Elemente nicht mehr erreichbar machen. Siehe auch Objektgrößen.

Wenn Sie einen Konstruktor maximieren, werden in der Ansicht Zusammenfassung alle zugehörigen Instanzen angezeigt. Für jede Instanz wird in den entsprechenden Spalten eine Aufschlüsselung der flachen und beibehaltenen Größen angezeigt. Die Zahl nach dem Zeichen @ ist die eindeutige ID des Objekts. Sie können damit Heap-Snapshots auf Objektbasis vergleichen.

Konstruktorfilter

In der Ansicht Zusammenfassung können Sie Konstruktoren nach häufigen Fällen ineffizienter Speichernutzung filtern.

Wenn Sie diese Filter verwenden möchten, wählen Sie in der Aktionsleiste im Drop-down-Menü ganz rechts eine der folgenden Optionen aus:

  • All Objects (Alle Objekte): alle Objekte, die vom aktuellen Snapshot erfasst wurden. Standardmäßig festgelegt.
  • Vor Snapshot 1 zugewiesene Objekte: Objekte, die erstellt wurden und vor dem ersten Snapshot im Arbeitsspeicher verbleiben.
  • Zwischen Snapshot 1 und Snapshot 2 zugewiesene Objekte: Hier sehen Sie den Unterschied zwischen den Objekten im jeweils letzten und im vorherigen Snapshot. Mit jedem neuen Snapshot wird der Drop-down-Liste ein Inkrement dieses Filters hinzugefügt.
  • Doppelte Strings: Stringwerte, die mehrmals im Arbeitsspeicher gespeichert wurden.
  • Von getrennten Knoten beibehaltene Objekte: Objekte, die aktiv bleiben, weil ein getrennter DOM-Knoten auf sie verweist.
  • Von der Entwicklertools-Konsole aufbewahrte Objekte: Objekte, die im Arbeitsspeicher gehalten werden, weil sie über die Entwicklertools-Konsole ausgewertet oder mit ihnen interagiert wurden.

Sondereinträge in der Zusammenfassung

In der Ansicht Zusammenfassung werden Objekte nicht nur nach Konstruktoren, sondern auch nach folgenden Kriterien gruppiert:

  • Integrierte Funktionen wie Array oder Object
  • HTML-Elemente, die nach ihren Tags gruppiert sind, z. B. <div>, <a> oder <img>.
  • Funktionen, die Sie in Ihrem Code definiert haben.
  • Spezielle Kategorien, die nicht auf Konstruktoren basieren.

Konstruktoreinträge

(array)

Diese Kategorie enthält verschiedene interne Array-ähnliche Objekte, die nicht direkt den in JavaScript sichtbaren Objekten entsprechen.

So wird beispielsweise der Inhalt von JavaScript-Array-Objekten in einem sekundären internen Objekt namens (object elements)[] gespeichert, um die Größe leichter ändern zu können. Ebenso werden die benannten Attribute in JavaScript-Objekten häufig in sekundären internen Objekten namens (object properties)[] gespeichert, die ebenfalls in der Kategorie (array) aufgeführt sind.

(compiled code)

Diese Kategorie enthält interne Daten, die V8 benötigt, um von JavaScript oder WebAssembly definierte Funktionen auszuführen. Jede Funktion kann auf verschiedene Arten dargestellt werden, von klein und langsam bis groß und schnell.

V8 verwaltet die Arbeitsspeichernutzung in dieser Kategorie automatisch. Wenn eine Funktion viele Male ausgeführt wird, benötigt V8 mehr Arbeitsspeicher für diese Funktion, damit sie schneller ausgeführt werden kann. Wenn eine Funktion längere Zeit nicht ausgeführt wurde, löscht V8 möglicherweise die internen Daten für diese Funktion.

(concatenated string)

Wenn V8 zwei Strings verkettet, z. B. mit dem JavaScript-Operator +, kann das Ergebnis intern als „verketteter String“ dargestellt werden. auch als Rope-Datenstruktur bezeichnet.

Anstatt alle Zeichen der beiden Quellstrings in einen neuen String zu kopieren, weist V8 ein kleines Objekt mit den internen Feldern first und second zu, die auf die beiden Quellstrings verweisen. So spart V8 Zeit und Arbeitsspeicher. Aus der Perspektive des JavaScript-Codes handelt es sich hierbei lediglich um normale Zeichenfolgen, die sich wie alle anderen Zeichenfolgen verhalten.

InternalNode

Diese Kategorie steht für Objekte, die außerhalb von V8 zugewiesen sind, z. B. durch Blink definierte C++-Objekte.

Um C++-Klassennamen zu sehen, verwenden Sie Chrome for Testing und gehen Sie so vor:

  1. Öffne die Entwicklertools und aktiviere die Einstellungen > Tests > Option zum Freigeben interner Daten in Heap-Snapshots anzeigen.
  2. Öffnen Sie den Bereich Arbeitsspeicher, wählen Sie Heap-Snapshot aus und aktivieren Sie das Interne Daten freigeben (mit zusätzlichen implementierungsspezifischen Details).
  3. Reproduzieren Sie das Problem, das dazu geführt hat, dass InternalNode viel Arbeitsspeicher einbehalten hat.
  4. Heap-Snapshot erstellen In diesem Snapshot haben Objekte C++ Klassennamen anstelle von InternalNode.
(object shape)

Wie unter Schnelle Eigenschaften in V8 beschrieben, verfolgt V8 verborgene Klassen (oder Formen), damit mehrere Objekte mit denselben Eigenschaften in derselben Reihenfolge effizient dargestellt werden können. Diese Kategorie enthält die ausgeblendeten Klassen system / Map (unabhängig von JavaScript Map) und die zugehörigen Daten.

(sliced string)

Wenn V8 einen Teilstring benötigt, z. B. wenn JavaScript-Code String.prototype.substring() aufruft, kann V8 ein Objekt vom Typ „geschnittener String“ zuweisen, anstatt alle relevanten Zeichen aus dem ursprünglichen String zu kopieren. Dieses neue Objekt enthält einen Zeiger auf die ursprüngliche Zeichenfolge und beschreibt, welcher Zeichenbereich aus der ursprünglichen Zeichenfolge verwendet werden soll.

Aus der Perspektive des JavaScript-Codes handelt es sich hierbei lediglich um normale Zeichenfolgen, die sich wie alle anderen Zeichenfolgen verhalten. Wenn ein segmentierter String sehr viel Arbeitsspeicher einbehält, hat das Programm möglicherweise Problem 2869 ausgelöst und es könnte von Vorteil sein, gezielte Schritte zur "Vereinfachung" auszuführen. die aufgeteilte Zeichenfolge.

system / Context

Interne Objekte des Typs system / Context enthalten lokale Variablen aus einem Closure, einem JavaScript-Bereich, auf den eine verschachtelte Funktion zugreifen kann.

Jede Funktionsinstanz enthält einen internen Zeiger auf die Context, in der sie ausgeführt wird, sodass sie auf diese Variablen zugreifen kann. Obwohl Context-Objekte nicht direkt in JavaScript sichtbar sind, haben Sie die direkte Kontrolle darüber.

(system)

Diese Kategorie enthält verschiedene interne Objekte, die (noch) nicht sinnvoller kategorisiert wurden.

Vergleichsansicht

In der Ansicht Vergleich können Sie durch den Vergleich mehrerer Snapshots gehackte Objekte finden. Wenn Sie beispielsweise eine Aktion ausführen und dann umgekehrt, z. B. ein Dokument öffnen oder schließen, sollten keine zusätzlichen Objekte zurückbleiben.

So prüfen Sie, ob ein bestimmter Vorgang keine Lecks verursacht:

  1. Erstellen Sie einen Heap-Snapshot, bevor Sie einen Vorgang ausführen.
  2. Führe einen Vorgang aus. Interagieren Sie also mit einer Seite auf eine Weise, die Ihrer Meinung nach ein Speicherleck verursachen könnte.
  3. Führe einen umgekehrten Vorgang durch. Führen Sie also die gegensätzliche Interaktion durch und wiederholen Sie dies einige Male.
  4. Erstellen Sie einen zweiten Heap-Snapshot und wählen Sie Vergleich aus. Snapshot 1.

In der Ansicht Vergleich sehen Sie den Unterschied zwischen zwei Snapshots. Beim Maximieren eines Gesamtwerts Eintragsinstanzen, hinzugefügte und gelöschte Objektinstanzen werden angezeigt:

Vergleich mit Snapshot 1.

Begrenzungsansicht

Die Ansicht Begrenzung bietet eine Vogelperspektive der Objektstruktur Ihrer Anwendung. Sie können einen Blick auf Funktions-Closures werfen, VM-interne Objekte beobachten, die zusammen Ihre JavaScript-Objekte ausmachen, und auf sehr niedriger Ebene nachvollziehen, wie viel Arbeitsspeicher Ihre Anwendung nutzt.

Die Ansicht bietet mehrere Einstiegspunkte:

  • DOMWindow-Objekte. Globale Objekte für JavaScript-Code
  • GC-Roots GC-Roots, die vom automatischen Speicherbereinigung der VM verwendet werden. GC-Roots können aus integrierten Objektzuordnungen, Symboltabellen, VM-Thread-Stacks, Kompilierungs-Caches, Handle-Bereichen und globalen Handles bestehen.
  • Native Objekte: Browserobjekte, die per Push übertragen wurden in der virtuellen JavaScript-Maschine, um Automatisierung z. B. für DOM-Knoten und CSS-Regeln zu ermöglichen.

Die Ansicht „Eingrenzung“.

Bereich „Retainer“

Im Bereich Retainer unten im Bereich Memory werden Objekte angezeigt, die auf das in der Ansicht ausgewählte Objekt verweisen. Im Bereich Speicher wird der Abschnitt Retainer aktualisiert, wenn Sie in einer der Ansichten außer Statistiken ein anderes Objekt auswählen.

Der Bereich „Retainers“.

In diesem Beispiel wird der ausgewählte String vom Attribut x einer Item-Instanz beibehalten.

Retainer ignorieren

Sie können Halterungen ausblenden, um herauszufinden, ob andere Objekte die ausgewählten beibehalten. Bei dieser Option müssen Sie nicht zuerst diesen Retainer aus dem Code entfernen und dann den Heap-Snapshot noch einmal erstellen.

&quot;Diesen Retainer ignorieren&quot; im Drop-down-Menü auswählen.

Wenn Sie einen Retainer ausblenden möchten, klicken Sie mit der rechten Maustaste darauf und wählen Sie Diesen Retainer ignorieren aus. Ignorierte Retainer sind in der Spalte Distance mit ignored gekennzeichnet. Wenn Sie nicht mehr alle Retainer ignorieren möchten, klicken Sie oben in der Aktionsleiste auf Ignorierte Retainer wiederherstellen.

Nach einem bestimmten Objekt suchen

Sie können mit Ctrl + Ctrl nach einem Objekt im erfassten Heap suchen und die Objekt-ID eingeben.

Namensfunktionen zur Unterscheidung von Schließungen

Es ist hilfreich, die Funktionen so zu benennen, dass Sie zwischen Schließungen im Snapshot unterscheiden können.

Im folgenden Code werden beispielsweise keine benannten Funktionen verwendet:

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

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

  return lC;
}

In diesem Beispiel ist das nicht der Fall:

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

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

  return lC;
}

Benannte Funktion in einem Abschluss.

DOM-Leaks aufdecken

Der Heap-Profiler kann bidirektionale Abhängigkeiten zwischen browsernativen Objekten (DOM-Knoten und CSS-Regeln) und JavaScript-Objekten widerspiegeln. So lassen sich sonst unsichtbare Lecks aufdecken, die durch vergessene, losgelöste DOM-Unterbäume verursacht werden.

DOM-Leaks können größer sein, als Sie denken. Dazu ein Beispiel: Wann wird der #tree-Speicher bereinigt?

  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 enthält einen Verweis auf sein übergeordnetes Element (parentNode) und rekursiv bis zu #tree. Nur wenn leafRef auf Null gesetzt wird, ist der gesamte Baum unter #tree ein Kandidat für die GC.

DOM-Unterstrukturen