Heap-Snapshots aufzeichnen

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Unter Arbeitsspeicher > Profile > Heap-Snapshot können Sie Heap-Snapshots aufzeichnen und Speicherlecks finden.

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

Schnappschuss aufnehmen

So erstellen Sie einen Heap-Snapshot:

  1. Öffnen Sie auf einer Seite, für die Sie ein Profil erstellen möchten, die Entwicklertools und den Bereich Arbeitsspeicher.
  2. Wählen Sie den Profiltyp Heap-Snapshot radio_button_checked und dann eine JavaScript-VM-Instanz aus. Klicken Sie dann auf Snapshot erstellen.

Ein ausgewählter Profilerstellungstyp und eine JavaScript-VM-Instanz.

Wenn der Snapshot im Bereich Arbeitsspeicher geladen und geparst wird, wird unter dem Snapshot-Titel im Bereich HEAP SNAPSHOTS die Gesamtgröße der erreichbaren JavaScript-Objekte angezeigt.

Die Gesamtgröße der erreichbaren Objekte.

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

Ein Heap-Snapshot verteilter Objektobjekte.

Snapshots löschen

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

Alle Profile löschen.

Snapshots anzeigen

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

Ansehen Inhalte Zweck
Zusammenfassung Objekte, die nach Konstruktornamen gruppiert sind. Damit können Sie nach Objekten und deren Speichernutzung nach Typ suchen. Hilfreich beim Verfolgen von DOM-Leaks.
Vergleich Unterschiede zwischen zwei Snapshots. Damit können Sie zwei (oder mehr) Snapshots vor und nach einem Vorgang vergleichen. Bestätigen Sie das Vorhandensein und die Ursache eines Speicherlecks, indem Sie das Delta des freigegebenen Arbeitsspeichers und der Referenzzahl untersuchen.
Begrenzung Heap-Inhalte Bietet einen besseren Überblick über die Objektstruktur und hilft bei der Analyse von Objekten, auf die im globalen Namespace (Fenster) verwiesen wird, um herauszufinden, was sie griffbereit hat. Damit kannst du Schließungen analysieren und Objekte auf niedriger Ebene genauer unter die Lupe nehmen.
Statistiken Kreisdiagramm der Arbeitsspeicherzuweisung Sehen Sie sich die reellen 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 dem die Konstruktoren in einer Spalte aufgeführt sind. Sie können Konstruktoren erweitern, um die Objekte zu sehen, die sie instanziiert haben.

Die Ansicht „Zusammenfassung“ mit einem erweiterten Konstruktor.

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

  • Distance gibt die Entfernung zur Wurzel an. Dazu wird der kürzeste einfache Pfad der Knoten verwendet.
  • Flache Größe gibt die Summe der flachen Größen aller Objekte an, die von einem bestimmten Konstruktor erstellt wurden. Die flache Größe ist die Größe des Arbeitsspeichers, den ein Objekt selbst enthält. Im Allgemeinen haben Arrays und Strings größere, flache Größen. Siehe auch Objektgrößen.
  • Unter Beibehaltene Größe sehen Sie die maximale beibehaltene Größe eines Satzes von Objekten. Die beibehaltene Größe ist die Größe des Arbeitsspeichers, den Sie durch Löschen eines Objekts freigeben können, sodass die zugehörigen Objekte nicht mehr erreichbar sind. Siehe auch Objektgrößen.

Wenn Sie einen Konstruktor maximieren, werden in der Zusammenfassungsansicht alle zugehörigen Instanzen angezeigt. Jede Instanz erhält eine Aufschlüsselung der oberflächlichen und beibehaltenen Größen in den entsprechenden Spalten. Die Zahl nach dem Zeichen @ ist die eindeutige ID des Objekts. Sie können damit Heap-Snapshots pro Objekt vergleichen.

Sondereinträge in der Zusammenfassung

Neben der Gruppierung nach Konstruktoren werden in der Ansicht Zusammenfassung Objekte auch nach folgenden Kriterien gruppiert:

  • Integrierte Funktionen wie Array oder Object
  • 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.

Beispielsweise werden die Inhalte von JavaScript-Array-Objekten in einem sekundären internen Objekt namens (object elements)[] gespeichert, um die Größenanpassung zu erleichtern. Ebenso werden die benannten Eigenschaften in JavaScript-Objekten oft in sekundären internen Objekten mit dem Namen (object properties)[] gespeichert, die auch in der Kategorie (array) aufgeführt sind.

(compiled code)

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

V8 verwaltet automatisch die Arbeitsspeichernutzung in dieser Kategorie. 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 über einen längeren Zeitraum 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“, auch Rope genannt, dargestellt werden.

Anstatt alle Zeichen der beiden Quellstrings in einen neuen String zu kopieren, weist V8 ein kleines Objekt mit internen Feldern namens first und second zu, die auf die beiden Quellstrings verweisen. Dadurch spart V8 Zeit und Arbeitsspeicher. Aus Sicht von JavaScript-Code sind dies nur normale Strings und verhalten sich wie jeder andere String.

InternalNode

Diese Kategorie stellt Objekte außerhalb von V8 dar, z. B. von Blink definierte C++-Objekte.

Wenn Sie C++-Klassennamen sehen möchten, verwenden Sie Chrome for Testing und gehen Sie so vor:

  1. Öffnen Sie die Entwicklertools und aktivieren Sie settings > Einstellungen > Tests > check_box Option zum Freigeben von internen Elementen in Heap-Snapshots anzeigen.
  2. Öffnen Sie das Steuerfeld Arbeitsspeicher, wählen Sie radio_button_checked Heap-Snapshot aus und aktivieren Sie radio_button_checked Interne Elemente sichtbar machen (enthält zusätzliche implementierungsspezifische Details).
  3. Reproduzieren Sie das Problem, das dazu geführt hat, dass InternalNode viel Arbeitsspeicher erhalten hat.
  4. Erstellen Sie einen Heap-Snapshot. In diesem Snapshot haben Objekte C++-Klassennamen anstelle von InternalNode.
(object shape)

Wie unter Schnelleigenschaften in V8 beschrieben, verfolgt V8 ausgeblendete Klassen (oder Formen), sodass mehrere Objekte mit denselben Eigenschaften in derselben Reihenfolge effizient dargestellt werden können. Diese Kategorie enthält die versteckten Klassen namens system / Map (unabhängig von JavaScript Map) und verwandte Daten.

(sliced string)

Wenn V8 einen Teilstring verwenden muss, z. B. wenn String.prototype.substring() durch JavaScript-Code aufgerufen wird, weist V8 ein aufgeteiltes String-Objekt zu, 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 Sicht von JavaScript-Code sind dies nur normale Strings und verhalten sich wie jeder andere String. Wenn ein geschnittener String viel Arbeitsspeicher zurückhält, hat das Programm möglicherweise Problem 2869 ausgelöst. In diesem Fall könnte es hilfreich sein, den geschnittenen String absichtlich zu vereinfachen.

system / Context

Interne Objekte vom Typ system / Context enthalten lokale Variablen aus einem Closure. Das ist ein 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, damit sie auf diese Variablen zugreifen kann. Obwohl Context-Objekte nicht direkt über JavaScript sichtbar sind, können Sie sie direkt steuern.

(system)

Diese Kategorie umfasst verschiedene interne Objekte, die (noch) nicht aussagekräftiger kategorisiert wurden.

Vergleichsansicht

In der Ansicht Vergleich können Sie mehrere Snapshots miteinander vergleichen, um gefundene Objekte zu finden. Wenn Sie beispielsweise eine Aktion ausführen und sie umkehren, z. B. ein Dokument öffnen und schließen, sollten keine zusätzlichen Objekte übrig bleiben.

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

  1. Erstellen Sie einen Heap-Snapshot, bevor Sie einen Vorgang ausführen.
  2. Vorgang ausführen Interagieren Sie mit einer Seite auf eine Art und Weise, die Ihrer Meinung nach die Ursache für ein Speicherleck sein könnte.
  3. Führen Sie eine umgekehrte Operation aus. Führen Sie also die gegenteilige Interaktion aus und wiederholen Sie sie einige Male.
  4. Erstellen Sie einen zweiten Heap-Snapshot, ändern Sie die Ansicht in Vergleich und vergleichen Sie ihn mit Snapshot 1.

Die Ansicht Vergleich zeigt den Unterschied zwischen zwei Snapshots. Beim Erweitern eines Gesamteintrags werden hinzugefügte und gelöschte Objektinstanzen angezeigt:

Vergleich zu Snapshot 1.

Begrenzungsansicht

Die Begrenzung bietet eine Vogelperspektive der Objektstruktur Ihrer Anwendung. Sie können einen Blick in Funktions-Closures werfen, interne VM-Objekte beobachten, die zusammen Ihre JavaScript-Objekte bilden, und auf sehr niedriger Ebene verstehen, wie viel Speicher Ihre Anwendung verwendet.

Die Ansicht bietet mehrere Einstiegspunkte:

  • DOMWindow-Objekte. Globale Objekte für JavaScript-Code.
  • GC-Roots. GC-Roots, die vom Garbage Collector der VM verwendet werden. GC-Root-Zertifikate können aus integrierten Objektzuordnungen, Symboltabellen, VM-Thread-Stacks, Kompilierungscaches, Handles und globalen Handles bestehen.
  • Native Objekte: Browserobjekte werden in die JavaScript-VM übertragen, um eine Automatisierung zu ermöglichen, z. B. DOM-Knoten und CSS-Regeln.

Die Begrenzungsansicht.

Der Abschnitt „Keeper“

Im Abschnitt Keeper unten im Bereich Arbeitsspeicher werden Objekte angezeigt, die auf das in der Ansicht ausgewählte Objekt verweisen.

Der Abschnitt „Keepers“.

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

Ein bestimmtes Objekt suchen

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

Benennen Sie Funktionen zur Unterscheidung von Schließungen

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

Der folgende Code verwendet beispielsweise keine benannten Funktionen:

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 geschieht 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-Lecks finden

Der Heap-Profiler kann bidirektionale Abhängigkeiten zwischen browsernativen Objekten (DOM-Knoten und CSS-Regeln) und JavaScript-Objekten widerspiegeln. So lassen sich ansonsten unsichtbare Speicherlecks erkennen, die durch vergessene getrennte DOM-Unterstrukturen entstehen, die um sie herum schweben.

DOM-Lecks können größer sein, als Sie denken. Dazu ein Beispiel: Wann wird die automatische Speicherbereinigung von #tree genommen?

  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 behält einen Verweis auf das übergeordnete Element (parentNode) und rekursiv bis zu #tree bei. Wenn leafRef also auf null gesetzt wird, ist der ganze Baum unter #tree ein Kandidat für die automatische Speicherbereinigung.

DOM-Unterstrukturen