Heap-Snapshots aufzeichnen

Meggin Kearney
Meggin Kearney
Sofia Emelianova
Sofia Emelianova

Hier erfahren Sie, wie Sie Heap-Snapshots unter Arbeitsspeicher > Profile > Heap-Snapshot aufzeichnen und Arbeitsspeicherlecks 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 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 radio_button_checked Heap-Snapshot aus, wählen Sie eine JavaScript-VM-Instanz aus und klicken Sie auf Snapshot erstellen.

Ein ausgewählter Profilerstellungstyp 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. 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 Blockieren 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 Inhalte 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:

  • Entfernung zeigt die Entfernung zum Stamm mithilfe des kürzesten einfachen Knotenpfads an.
  • Flache Größe zeigt die Summe der flachen Größen aller Objekte, die von 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 anhand von 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 Snapshots 1 und Snapshots 2 zugewiesene Objekte: Sehen Sie sich die Differenz der Objekte zwischen dem letzten und dem vorherigen Snapshot an. Mit jedem neuen Snapshot wird der Drop-down-Liste ein Inkrement dieses Filters hinzugefügt.
  • Duplizierte 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 aufbewahrt werden, weil sie über die Entwicklertools-Konsole ausgewertet oder mit ihnen interagiert wurden.

Sondereinträge in der Zusammenfassung

In der Ansicht Summary werden Objekte nicht nur nach Konstruktoren, sondern 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 enthält 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 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 Einstellungen Einstellungen > Tests > Kästchen Option zum Anzeigen interner Elemente in Heap-Snapshots.
  2. Öffnen Sie den Bereich Arbeitsspeicher, wählen Sie radio_button_checked Heap-Snapshot aus und aktivieren Sie das radio_button_checked 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 verwenden muss, z. B. wenn JavaScript-Code String.prototype.substring() aufruft, weist V8 möglicherweise ein aufgeteilten 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 der Perspektive des JavaScript-Codes handelt es sich hierbei lediglich um normale Zeichenfolgen, die sich wie alle anderen Zeichenfolgen verhalten. Wenn ein aufgeteilter String viel Arbeitsspeicher beibehält, hat das Programm möglicherweise das Problem 2869 ausgelöst und es könnte von Vorteil sein, den aufgeteilten String gezielt zu „vereinfachen“.

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 die Datenlecks finden, indem Sie mehrere Snapshots miteinander vergleichen. Wenn Sie beispielsweise eine Aktion ausführen und dann umgekehrt, z. B. ein Dokument öffnen und schließen, sollten keine zusätzlichen Objekte zurückbleiben.

So stellen Sie sicher, dass bei einem bestimmten Vorgang keine Datenlecks entstehen:

  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, ändern Sie seine Ansicht in Vergleich und vergleichen Sie ihn mit Snapshot 1.

In der Ansicht Vergleich sehen Sie den Unterschied zwischen zwei Snapshots. Beim Maximieren einer Summeneintrag werden hinzugefügte und gelöschte Objektinstanzen 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 in die virtuelle JavaScript-Maschine verschoben werden, um eine Automatisierung zu ermöglichen, z. B. DOM-Knoten und CSS-Regeln.

Die Ansicht „Begrenzung“.

Der Bereich „Retainers“

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 Retainer ausblenden, um herauszufinden, ob andere Objekte das ausgewählte Objekt beibehalten. Bei dieser Option müssen Sie nicht zuerst diesen Retainer aus dem Code entfernen und dann den Heap-Snapshot noch einmal erstellen.

Die Option "Diesen Retainer ignorieren" im Dropdown-Menü.

Um einen Retainer auszublenden, klicken Sie mit der rechten Maustaste 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 playlist_remove Ignorierte Retainer wiederherstellen.

Nach einem bestimmten Objekt suchen

Sie können mit Strg + F 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 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 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 ansonsten unsichtbare Lecks erkennen, die durch vergessene losgelöste DOM-Unterstrukturen entstehen.

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

DOM-Unterstrukturen