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 Arbeitsspeicherverteilung für die JavaScript-Objekte und die zugehörigen DOM-Knoten Ihrer Seite an. Sie können damit JS-Heap-Snapshots erstellen, Speicherdiagramme analysieren, Snapshots vergleichen und Speicherlecks finden. Weitere Informationen finden Sie unter Objektbeibehaltungsstruktur.

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.

Snapshots zeigen nur die Objekte aus dem Speicherdiagramm an, die über das globale Objekt erreichbar sind. Das Erstellen eines Snapshots beginnt immer mit der Garbage Collection.

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

Snapshots löschen

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

Löschen Sie alle Profile.

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. Mit dieser Funktion können Sie nach Objekten und ihrer Speichernutzung nach Typ suchen. Hilfreich zum Nachverfolgen von DOM-Lecks.
Vergleich Unterschiede zwischen zwei Snapshots. Damit können Sie zwei oder mehr Snapshots vor und nach einer Operation 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.

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

Zusammenfassung

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

Die Zusammenfassungsansicht mit einem maximierten Konstruktor

Wenn Sie irrelevante Konstruktoren herausfiltern möchten, geben Sie oben in der Ansicht Zusammenfassung in den Klassenfilter 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:

  • Entfernung zeigt die Entfernung zum Stamm mithilfe des kürzesten einfachen Knotenpfads an.
  • Größe ohne Unterobjekte ist die Summe der Größen ohne Unterobjekte aller Objekte, die mit einem bestimmten Konstruktor erstellt wurden. Die Shallow-Größe ist die Größe des Arbeitsspeichers, der von einem Objekt selbst belegt wird. Arrays und Strings haben in der Regel eine größere flache Größe. Siehe auch Objektgrößen.
  • Beibehaltene Größe zeigt die maximale beibehaltene Größe innerhalb desselben Satzes 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 seine 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. Damit können Sie Heap-Snapshots objektweise vergleichen.

Konstruktorfilter

In der Ansicht Zusammenfassung können Sie Konstruktoren anhand von 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.
  • Objekte, die vor Snapshot 1 zugewiesen wurden: Objekte, die erstellt wurden und im Arbeitsspeicher verblieben, bevor der erste Snapshot aufgenommen wurde.
  • 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> oder <img>.
  • 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 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 unterschiedliche Weise dargestellt werden, von klein und langsam bis groß und schnell.

V8 verwaltet die Speichernutzung 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 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 umfasst Objekte, die außerhalb von V8 zugewiesen werden, z. B. C++-Objekte, die von Blink definiert werden.

Wenn Sie C++-Klassennamen sehen möchten, verwenden Sie Chrome for Testing und führen Sie die folgenden Schritte aus:

  1. Öffnen Sie die DevTools und aktivieren Sie Einstellungen > Experimente > Option zum Anzeigen von internen Informationen in Heap-Snapshots anzeigen.
  2. Öffnen Sie den Bereich Speicher, wählen Sie Heap-Snapshot aus und aktivieren Sie das Kästchen Interne Informationen offenlegen (enthalten zusätzliche implementierungsspezifische 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 vom Typ 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ühren Sie 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 gegensätzliche Interaktion durch und wiederholen Sie dies einige Male.
  4. Erstellen Sie einen zweiten Heap-Snapshot und ändern Sie die Ansicht in Vergleich. 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 eine Vogelperspektive der 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-Roots, die vom automatischen Speicherbereinigung der VM verwendet werden. GC-Wurzeln können aus integrierten Objektkarten, Symboltabellen, VM-Thread-Stacks, Kompilierungscaches, 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“.

Bereich „Retainer“

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 „Retainers“.

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 &quot;Diesen Retainer ignorieren&quot; im Dropdown-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 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 Closure.

DOM-Lecks aufdecken

Der Heap-Profiler kann bidirektionale Abhängigkeiten zwischen browsereigenen 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 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