Heap-Snapshots aufzeichnen

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Weitere Informationen zum Erfassen von Heap-Snapshots mit Arbeitsspeicher > Profile > Heap-Snapshot und zum Auffinden von Speicherlecks

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 Heap-Snapshot-Profilierungstyp aus, wählen Sie dann 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. Das Erstellen eines Snapshots beginnt immer mit der Garbage Collection.

Ein Heap-Snapshot von verstreuten Artikelobjekten.

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 und zu verschiedenen Zwecken 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. Hilfreich zum Nachverfolgen von DOM-Lecks.
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 die Differenz zwischen freigegebenem Arbeitsspeicher und Referenzzählung prüfen.
Begrenzung Heap-Inhalte Bietet eine bessere Übersicht über die Objektstruktur und hilft bei der Analyse von Objekten, auf die im globalen Namespace (Fenster) verwiesen wird, um herauszufinden, was sie am Leben hält. Damit können Sie Schleifen analysieren und Ihre Objekte auf niedriger Ebene untersuchen.
Statistiken Kreisdiagramm zur Arbeitsspeicherzuweisung Sehen Sie sich die tatsächlichen Größen von Speicherteilen an, die Code, Strings, JS-Arrays, typisierten Arrays und Systemobjekten zugewiesen sind.

Die Zusammenfassungsansicht, die oben im Drop-down-Menü ausgewählt wurde.

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 maximieren, um die Objekte zu sehen, die sie instanziiert haben.

Die Zusammenfassungsansicht 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 mit dem Konstruktor erstellten Objekte an. In der Ansicht Zusammenfassung sind außerdem die folgenden Spalten zu sehen:

  • 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 in der Regel eine größere flache Größe. Siehe auch Objektgrößen.
  • Beibehaltene Größe gibt die maximale beibehaltene Größe derselben Gruppe von Objekten an. Die verbleibende Größe ist die Größe des Arbeitsspeichers, die Sie freigeben können, indem Sie ein Objekt löschen und seine abhängigen Objekte nicht mehr erreichbar machen. Siehe auch Objektgrößen.

Wenn Sie einen Konstruktor maximieren, werden in der Ansicht Zusammenfassung alle seine Instanzen angezeigt. Für jede Instanz wird in den entsprechenden Spalten eine Aufschlüsselung der schmäleren und der beibehaltenen Größen angezeigt. Die Zahl nach dem Zeichen @ ist die eindeutige ID des Objekts. Damit können Sie Heap-Snapshots objektweise 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 rechten Drop-down-Menü eine der folgenden Optionen aus:

  • Alle Objekte: alle Objekte, die im 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 weiterer Wert dieses Filters hinzugefügt.
  • Doppelte Strings: Stringwerte, die mehrmals im Arbeitsspeicher gespeichert wurden.
  • Von getrennten Knoten aufbewahrte Objekte: Objekte, die beibehalten werden, 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>, <img> usw.
  • Funktionen, die Sie in Ihrem Code definiert haben.
  • Spezielle Kategorien, die nicht auf Konstruktoren basieren.

Konstruktoreinträge

(array)

Diese Kategorie umfasst 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 Eigenschaften in JavaScript-Objekten häufig in sekundären internen Objekten mit dem Namen (object properties)[] gespeichert, die ebenfalls in der Kategorie (array) aufgeführt sind.

(compiled code)

Diese Kategorie umfasst interne Daten, die V8 benötigt, um Funktionen auszuführen, die in JavaScript oder WebAssembly definiert sind. Jede Funktion kann auf unterschiedliche Weise dargestellt werden, von klein und langsam bis groß und schnell.

V8 verwaltet die Arbeitsspeichernutzung in dieser Kategorie automatisch. Wenn eine Funktion häufig ausgeführt wird, verwendet 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 zusammenführt, z. B. mit dem JavaScript-Operator +, kann das Ergebnis intern als „zusammengesetzter String“ dargestellt werden, auch bekannt als Rope-Datenstruktur.

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 Sicht von JavaScript-Code sind dies ganz normale Strings, die sich wie jeder andere String 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 Einstellungen > Tests > Option zum Anzeigen interner Elemente in Heap-Snapshots.
  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 die InternalNode viel Arbeitsspeicher belegt.
  4. Erstellen Sie einen Heap-Snapshot. In diesem Snapshot haben Objekte C++ Klassennamen anstelle von InternalNode.
(object shape)

Wie unter Schnelle Eigenschaften in V8 beschrieben, überwacht V8 ausgeblendete Klassen (oder Formen), damit mehrere Objekte mit denselben Eigenschaften in derselben Reihenfolge effizient dargestellt werden können. Diese Kategorie enthält diese versteckten Klassen, die system / Map (nicht zu verwechseln mit JavaScript Map) heißen, und zugehörige Daten.

(sliced string)

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

Aus Sicht von JavaScript-Code sind dies ganz normale Strings, die sich wie jeder andere String verhalten. Wenn ein gesplitterter String viel Arbeitsspeicher belegt, hat das Programm möglicherweise Problem 2869 ausgelöst. In diesem Fall kann es sinnvoll sein, den gesplitterten String zu „flechten“.

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 Verweis auf den Context, in dem sie ausgeführt wird, damit sie auf diese Variablen zugreifen kann. Auch wenn Context-Objekte in JavaScript nicht direkt 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 rückgängig machen, z. B. ein Dokument öffnen und 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 auf eine Weise mit einer Seite, die Ihrer Meinung nach zu einem Datenleck führen könnte.
  3. Führen Sie einen umgekehrten Vorgang aus. Führen Sie also die entgegengesetzte Interaktion aus und wiederholen Sie sie einige Male.
  4. Erstellen Sie einen zweiten Heap-Snapshot, ändern Sie seine Ansicht in Vergleich und vergleichen Sie ihn mit Snapshot 1.

In der Ansicht Vergleich sehen Sie den Unterschied zwischen zwei Snapshots. Wenn Sie einen Gesamteintrag maximieren, werden hinzugefügte und gelöschte Objektinstanzen angezeigt:

Im Vergleich zu Snapshot 1.

Ansicht „Eindämmung“

Die Ansicht Begrenzung bietet einen Überblick über die Objektstruktur Ihrer Anwendung. Sie können sich Funktionsschließungen ansehen, interne VM-Objekte beobachten, die zusammen Ihre JavaScript-Objekte bilden, und auf sehr niedriger Ebene nachvollziehen, wie viel Arbeitsspeicher Ihre Anwendung benötigt.

Die Ansicht bietet mehrere Einstiegspunkte:

  • DOMWindow-Objekte Globale Objekte für JavaScript-Code
  • GC-Roots GC-Wurzeln, die vom Garbage Collector 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 in die JavaScript-virtuelle Maschine „geschoben“ werden, um Automatisierung zu ermöglichen, z. B. DOM-Knoten und CSS-Regeln.

Die Ansicht „Eingrenzung“.

Der Bereich „Retainers“

Im Bereich Retainers (Behaltene Objekte) unten im Bereich Memory (Speicher) werden Objekte angezeigt, die auf das in der Ansicht ausgewählte Objekt verweisen. Im Bereich Speicher wird der Bereich Besitzer aktualisiert, wenn Sie in einer der Ansichten (außer Statistiken) andere Objekte auswählen.

Der Bereich „Retainer“

In diesem Beispiel wird der ausgewählte String durch die x-Eigenschaft einer Item-Instanz beibehalten.

Retainer ignorieren

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

Die Option „Diesen Vertrag ignorieren“ im Drop-down-Menü.

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 Entfernung als ignored gekennzeichnet. Wenn du alle Abos wieder aktivieren möchtest, klicke oben in der Aktionsleiste auf Ignored retainers restore (Ignorierte Abos wiederherstellen).

Bestimmtes Objekt finden

Wenn Sie ein Objekt im erfassten Heap finden möchten, können Sie mit Strg + F nach der Objekt-ID suchen.

Funktionen benennen, um Schleifen zu unterscheiden

Es ist sehr hilfreich, die Funktionen zu benennen, damit Sie im Snapshot zwischen den Schließungen 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 gilt Folgendes:

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 Closure.

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 ansonsten unsichtbare Lecks erkennen, die durch vergessene losgelöste DOM-Unterstrukturen entstehen.

DOM-Lecks können größer sein, als Sie denken. Dazu ein Beispiel: Wann wird der #tree-Speicher automatisch 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-Unterbäume