Heim  >  Artikel  >  Web-Frontend  >  Grafische Einführung in die JavaScript-Speicheranalyse in den Chrome Developer Tools

Grafische Einführung in die JavaScript-Speicheranalyse in den Chrome Developer Tools

黄舟
黄舟Original
2017-03-14 15:28:102215Durchsuche

Ein Speicherverlust ist eine allmähliche Verringerung des verfügbaren Speichers eines Computers. Es tritt auf, wenn ein Programm den von ihm verwendeten temporären Speicher dauerhaft nicht freigibt. Bei JavaScript-Webanwendungen treten häufig auch speicherbezogene Probleme auf, die in nativen Anwendungen auftreten, wie z. B. Lecks. Webanwendungen müssen auch mit Garbage-Collection-Pausen umgehen .

Obwohl JavaScript die Garbage Collection für die automatische Speicherverwaltung verwendet, ist eine effektive Speicherverwaltung dennoch wichtig. In diesem Artikel befassen wir uns mit der Analyse von Speicherproblemen in JavaScript-Webanwendungen. Probieren Sie beim Kennenlernen der Funktionen unbedingt die Beispiele aus, um Ihr Verständnis dafür zu verbessern, wie diese Tools in der Praxis funktionieren. Bitte lesen Sie die Seite „Memory 101“, um sich mit der in diesem Artikel verwendeten Terminologie vertraut zu machen. Hinweis: Einige der Funktionen, die wir verwenden werden, sind derzeit nur in der Chrome Canary-Version des Browsers verfügbar. Wir empfehlen die Verwendung dieser Version, um die besten Tools zur Analyse der Speicherprobleme Ihrer Anwendung zu erhalten.

Fragen, über die Sie nachdenken müssen

Wenn Sie glauben, auf ein Speicherverlustproblem gestoßen zu sein, müssen Sie im Allgemeinen über drei Fragen nachdenken:

  • Beansprucht meine Seite zu viel Speicher? - Das TimelineZeilenspeicheransichtstool (Timeline-Speicheransicht) und der Chrome-Task-Manager (Chrome-Task-Manager) können Ihnen dabei helfen, zu bestätigen, ob Sie mehr Speicher verwendet haben . Die Speicheransicht kann die Anzahl der DOM-Knoten , die Anzahl der Dokumentes und die Anzahl der JS-Ereignisse beim Seitenrendering verfolgen. Als Faustregel gilt: Vermeiden Sie Referenzen auf DOM-Elemente, die Sie nicht mehr benötigen, entfernen Sie unnötige Ereignis-Listener und seien Sie vorsichtig, wenn Sie große Datenmengen speichern, die Sie möglicherweise nicht verwenden.

  • Hat meine Seite einen Speicherverlust? - ObjektZuordnungsverfolgung(Objekt allelocation tracker) hilft Ihnen, Lecks zu lokalisieren, indem es die Zuordnung von JS-Objekten in Echtzeit anzeigt. Sie können auch den Heap-Analysator (Heap Profiler) verwenden, um JS-Heap-Snapshots zu generieren und Objekte herauszufinden, die nicht durch Garbage Collection bereinigt wurden, indem Sie die Speicherzuordnung analysieren und die Unterschiede zwischen den Snapshots vergleichen.

  • Wie oft wird der Müll meiner Seite gesammelt? – Wenn Ihre Seite häufig Müll gesammelt wird, bedeutet das, dass Ihre Seite möglicherweise zu häufig zugewiesen wird. Die Timeline-Speicheransicht kann Ihnen dabei helfen, interessante Pausen zu finden.

Terminologie und Grundkonzepte

Dieser Abschnitt wird in Speicheranalyse eingeführt Allgemeine Begriffe, die in Speicheranalysetools für andere Sprachen verwendet werden. Die hier aufgeführten Begriffe und Konzepte werden im Heap Profiler-UI-Tool und in der zugehörigen Dokumentation verwendet.

Diese können uns helfen, uns mit der effektiven Nutzung von Gedächtnisanalysetools vertraut zu machen. Wenn Sie jemals Speicheranalysetools für Sprachen wie Java, .NET usw. verwendet haben, dann ist dies eine Rezension.

Objektgrößen

Stellen Sie sich Speicher als eine Sammlung von Grundtypen (wie Zahlen und Strings) und Objekten (assoziative Arrays) vor. Es könnte etwa wie das folgende Diagramm einer Reihe zusammenhängender Punkte aussehen.

Ein Objekt hat zwei Möglichkeiten, Speicher zu nutzen:

  • Das Objekt selbst verwendet direkt

  • Behalten Sie implizit Verweise auf andere Objekte bei. Diese Methode verhindert, dass die Garbage Collection (GC) diese Objekte automatisch recycelt.

Wenn Sie den Heap Profiler in DevTools verwenden (Heap Profiler, ein Tool zur Analyse von Speicherproblemen, auf der Registerkarte „Profil“ von DevTools), werden Sie möglicherweise überrascht sein, Spalten zu finden, die verschiedene Informationen anzeigen. Zwei davon sind: Direkte Speicherbelegung (Flache Größe) und Gesamte Speicherbelegung (Beibehaltene Größe) Was bedeuten sie also?

Speicher direkt belegen (Flache Größe, ausgenommen der vom referenzierten Objekt belegte Speicher)

Dies ist der vom Objekt selbst belegte Speicher.

Ein typisches JavaScript-Objekt verfügt über reservierten Speicher, um das Objekt zu beschreiben und seinen direkten Wert zu speichern. Im Allgemeinen belegen nur Arrays und Zeichenfolgen erheblich den Speicher (flache Größe). Aber Strings und Arrays speichern oft den Hauptdatenteil im Renderer-Speicher und legen nur ein kleines Wrapper-Objekt im JavaScript-Objektstapel offen.

Renderer-Speicher bezieht sich auf den gesamten Speicher, der im Renderprozess der von Ihnen analysierten Seite verwendet wird: der Speicher der Seite selbst + der vom JS-Heap in der Seite verwendete Speicher + die zugehörigen ausgelösten Arbeitsprozesse (Worker). von der Seite Der vom JS-Heap verwendete Speicher. Allerdings kann ein kleines Objekt indirekt viel Speicher belegen, indem es andere Objekte daran hindert, automatisch in den Müll zu gelangen.

Gesamt belegter Speicher (behaltene Größe, einschließlich des von referenzierten Objekten belegten Speichers)

Sobald ein Objekt gelöscht ist, können die abhängigen Objekte, auf die es verweist, nicht GC sein root (GC root) verweist darauf und der von ihnen belegte Speicher wird freigegeben. Der von einem Objekt belegte Gesamtspeicher umfasst den von diesen abhängigen Objekten belegten Speicher.

GC-Root besteht aus Controllern(handles) Diese Controller (ob lokal oder global) wird erstellt, wenn durch die integrierteFunktion(nativer Code) ein Verweis auf ein JavaScript-Objekt außerhalb der V8-Engine erstellt wird. Alle diese Controller können in den GC-Wurzeln (GC-Wurzeln) > Handle-Bereich und GC-Wurzeln >Globalen Handlern gefunden in . Die Einführung dieser Controller in diesem Artikel kann ohne ein tiefes Verständnis der Browser-Implementierung verwirrend sein. Sie müssen sich nicht zu viele Gedanken über GC-Roots und Controller machen.

Es gibt viele interne GC-Wurzeln, die für den Benutzer unwichtig sind. Aus Anwendungssicht gibt es folgende Situationen:

Hinweis: Wir empfehlen Benutzern, beim Erstellen von Heap-Snapshots keinen Code in der Konsole auszuführen oder Debugging-Haltepunkte zu aktivieren. Die

-Speicherzuordnung beginnt mit einem Root, bei dem es sich um das

-Objekt des Browsers oder das windowNode.js-Modul--Objekt handeln kann. Wie diese Objekte aus dem Speicher zurückgewonnen werden, unterliegt nicht der Kontrolle des Benutzers. Global

Objekte, die nicht vom GC-Stamm durchlaufen werden können, werden im Speicher recycelt.

Hinweis: Die Daten in den Feldern „Direkter Speicher belegt“ und „Gesamtspeicher belegt“ werden in Bytes ausgedrückt.

Gesamtspeicherbaum, der von Objekten belegt ist

Wir haben zuvor gelernt, dass der Heap eine Netzwerkstruktur ist, die aus verschiedenen miteinander verbundenen Objekten besteht. In der digitalen Welt wird diese Struktur als

Graph oder Memory Graph bezeichnet. Diagramme bestehen aus Knoten, die durch Kanten verbunden sind, und sie sind alle beschriftet.

  • Knoten ( oder Objekt) Die Tag-Namen von Knoten werden durch den Konstruktorden Namen der Funktion bestimmt wird bestimmt

  • Kanten Der Labelname ist der Name des Attributs

Später in In diesem Dokument erfahren Sie, wie Sie mit dem Heap-Analysator Snapshots erstellen. Aus dem vom Heap-Analysator in der Abbildung unten generierten Schnappschuss können wir das Distanzfeld sehen: Es bezieht sich auf die Distanz vom Objekt zur GC-Wurzel. Wenn sich alle Objekte desselben Typs im gleichen Abstand befinden, eine kleine Teilmenge jedoch weiter voneinander entfernt ist, liegt möglicherweise ein Fehler vor, den Sie untersuchen müssen.

Dominatoren

Dominatoren sind wie eine Baumstruktur, da jedes Objekt einen Dominator hat. Der Controller eines Objekts verweist möglicherweise nicht direkt auf das von ihm dominierte Objekt, d. h. die Baumstruktur des dominierten Objekts ist kein Spanning Tree im Diagramm.

Im Bild oben:

  • Knoten 1 dominiert Knoten 2

  • Knoten 2 dominiert die Knoten 3, 4 und 6

  • Knoten 3 dominiert Knoten 5

  • Knoten 5 dominiert Knoten 8

  • Knoten 6 dominiert Knoten 7

Im Beispiel unten ist Knoten #3 der dominierende Spieler von #10, aber #7 ist auch der dominierende Knoten in allen GC zu #10 erscheint in den Pfaden. Wenn also das B-Objekt in jedem Pfad vom Wurzelknoten zum A-Objekt erscheint, dann ist das B-Objekt das dominierende Objekt des A-Objekts.

V8-Einführung

In diesem Abschnitt beschreiben wir einige speicherbezogene Konzepte, die sich auf die V8 JavaScript Virtual Machine beziehen (V8 VM oder VM) bezogen. Bei der Speicheranalyse ist es hilfreich, diese Konzepte zu verstehen, um Heap-Snapshots zu verstehen.

JavaScript-Objektbeschreibung

hat drei primitive Typen:

  • Zahlen (z. B. 3.14159..)

  • Boolesche Werte (wahr oder falsch)

  • Zeichentyp (Strings) (z. B. „Werner Heisenberg“)

Sie verweisen nicht auf andere Werte, sondern sind nur Blattknoten oder Endknoten.

Zahlen werden auf eine von zwei Arten gespeichert:

  • 31-Bit-Ganzzahldirekter Wert, aufgerufen: kleine Ganzzahlen (kleine Ganzzahlens)(SMIs) oder

  • Heap-Objekte, referenziert als Heap-Wert . Heap-Werte werden zum Speichern von Daten verwendet, die nicht für die SMI-Speicherung geeignet sind, z. B. double, oder wenn ein Wert geboxt sein muss, z. B. dieser Wert, und dann festgelegt werden muss Attributwert.

Zeichendaten werden auf die folgenden zwei Arten gespeichert:

  • VM-Heap, Oder

  • im externen Renderer-Speicher. Zu diesem Zeitpunkt wird ein Wrapper-Objekt erstellt, um auf den Speicherort zuzugreifen, z. B. auf Skriptressourcen und andere im Webseitenpaket gespeicherte Inhalte, anstatt direkt auf den VM-Heap kopiert zu werden.

Neu erstellten JavaScript-Objekten wird Speicher auf dem JavaScript-Heap (oder VM-Heap) zugewiesen. Diese Objekte werden vom Garbage Collector von V8 verwaltet und bleiben im Speicher, solange ein starker Verweis auf sie besteht.

Lokale Objekte sind alle Objekte, die sich nicht im JavaScript-Heap befinden. Im Gegensatz zu Heap-Objekten werden sie während ihres Lebenszyklus nicht durch Müll gesammelt. Der Handler kann dies nur tun Objektreferenzen über JavaScript umschließen.

Verbindungszeichenfolge ist ein Objekt, das durch Zusammenführen eines Zeichenkettenpaars entsteht und das Ergebnis der Zusammenführung ist. Verbindungszeichenfolgen werden nur bei Bedarf zusammengeführt. Wie bei einer verketteten Zeichenfolge muss eine Teilzeichenfolge erstellt werden.

Zum Beispiel: Wenn Sie a und b verketten, erhalten Sie die Zeichenfolge (a, b), die zur Darstellung des Ergebnisses der Verkettung verwendet wird. Wenn Sie dieses Ergebnis später mit d verketten, erhalten Sie eine weitere verkettete Zeichenfolge ((a, b), d).

Array (Arrays) – Arrays sind Objekte mit numerischen Tasten. Sie werden häufig im V8-Motor verwendet, wenn große Datenmengen gespeichert werden. Objekte mit Schlüssel-Wert-Paaren wie Wörterbücher werden mithilfe von Arrays implementiert.

Ein typisches JavaScript-Objekt kann in einem von zwei Array-Typen gespeichert werden:

  • benannte Eigenschaften und

  • Zahlenelemente

Wenn nur wenige Eigenschaften vorhanden sind, werden diese direkt im JavaScript-Objekt selbst gespeichert.

Map – Ein Objekt, das zur Beschreibung eines Objekttyps und seiner Struktur verwendet wird. Karten werden beispielsweise verwendet, um die Struktur von Objekten zu beschreiben, um einen schnellen Zugriff auf Objekteigenschaften zu ermöglichen.

Objektgruppen

Jede lokale Objektgruppe besteht aus einer Reihe miteinander verbundener Objekte von. Beispielsweise kann in einem DOM-Unterbaum jeder Knoten auf sein übergeordnetes Element, das nächste untergeordnete Element und das nächste nebengeordnete Element zugreifen, die einen Assoziationsgraphen bilden. Beachten Sie, dass native Elemente nicht im JavaScript-Heap dargestellt werden. Deshalb ist ihre Größe Null, während ihr Wrapping-Objekt erstellt wird.

Jedes Wrapper-Objekt verfügt über einen Verweis auf das lokale Objekt, der zum Übergeben von Operationen an diesen lokalen Objekten verwendet wird. Diese lokalen Objekte verfügen auch über Verweise auf die umschlossenen Objekte. Dadurch entsteht jedoch keine unwiederbringliche Schleife, der GC ist intelligent genug, um lokale Objekte zu identifizieren, die keinen Verweis mehr auf das umschlossene Objekt haben, und diese freizugeben. Wenn ein Wrapper-Objekt jedoch nicht freigegeben wird, behält es alle Objektgruppen und zugehörigen Wrapper-Objekte bei.

Voraussetzungen und hilfreiche Tipps

Chrome Task Manager

Hinweis: Wenn Sie Chrome für die Speicherprofilierung verwenden, ist es am besten, eine saubere Testumgebung einzurichten

Öffnen Sie den Speichermanager von Chrome, beobachten Sie das Speicherfeld und führen Sie entsprechende Vorgänge auf einer Seite aus. Sie können schnell feststellen, ob dieser Vorgang dazu führt, dass die Seite viel Speicher belegt. Sie finden Memory Manager über das Chrome-Menü > Extras oder durch Drücken von Umschalt + Esc.

Klicken Sie nach dem Öffnen mit der rechten Maustaste auf die Kopfzeile und wählen Sie die Option „Von JavaScript verwendeter Speicher“ aus.

Speicherprobleme mithilfe der DevTools-Zeitleiste lokalisieren

Der erste Schritt zur Lösung des Problems besteht darin, nachweisen zu können, dass das Problem existiert. Dazu ist die Erstellung eines reproduzierbaren Tests erforderlich, der als Basismaß für das Problem dient. Ohne reproduzierbare Verfahren kann das Problem nicht zuverlässig gemessen werden. Mit anderen Worten: Ohne eine Vergleichsbasis gibt es keine Möglichkeit herauszufinden, welche Änderungen das Problem verursacht haben.

Das Zeitleistenfenster ist sehr hilfreich, um herauszufinden, wann ein Problem mit dem Programm vorliegt. Es zeigt die Momente an, in denen Ihre Webanwendung oder Website geladen wird und interagiert. Alle Ereignisse: vom Laden von Ressourcen über die Dekodierung von JavaScript, Stilberechnungen, Garbage-Collection-Pausen bis hin zum Neuzeichnen von Seiten. Alle sind auf der Zeitleiste dargestellt.

Bei der Analyse von Speicherproblemen kann die Speicheransicht im Zeitleistenfenster verwendet werden, um Folgendes zu beobachten:

    Gesamtspeicher verwendet – Hat sich die Speichernutzung erhöht? ?
  • Anzahl der DOM-Knoten
  • Anzahl der Dokumente
  • Anzahl der registrierten Ereignis-Listener

Weitere Informationen zum Auffinden von Speicher während der Speicheranalyse. Informationen zu Lecks finden Sie in Zack Grossbarts „Memory Profiling with the Chrome DevTools“

Proving the Vorliegen eines Problems

Das erste, was Sie tun müssen, ist herauszufinden, was Ihrer Meinung nach das Problem verursachen könnte. Einige Maßnahmen bei Speicherlecks. Dies kann jedes Ereignis sein, das auf der Seite auftritt, einschließlich Mouseovers, Klicks oder anderen Interaktionen, die zu Leistungseinbußen auf der Seite führen können.

Starten Sie die Aufnahme im Zeitleistenfenster (Strg+E oder Befehl+E) und führen Sie dann die Aktion aus, die Sie testen möchten. Um die Speicherbereinigung zu erzwingen, klicken Sie im Bedienfeld auf das Papierkorbsymbol (

).

Hier ist ein Beispiel für einen Speicherverlust, bei dem einige Punkte nicht durch Garbage Collection erfasst werden:

Wenn Sie nach einigen wiederholten Tests gezackte Bilder sehen ( über dem Speicherfeld) weist darauf hin, dass Ihr Programm viele kurzlebige Objekte enthält. Und wenn eine Reihe von Aktionen den Speicher nicht innerhalb eines bestimmten Bereichs hält und die Anzahl der DOM-Knoten nicht wieder auf die Anfangszahl zurückkehrt, können Sie einen Speicherverlust vermuten.

Sobald Sie ein Speicherproblem identifiziert haben, können Sie den

Heap-Analysator im

ProfilbereichProfiler) verwenden, um das Problem zu lokalisieren Ursache des Problems. Beispiel: Probieren Sie das Gedächtniswachstumsbeispiel aus, das Ihnen dabei helfen kann, Gedächtnisprobleme anhand der Zeitleiste effektiv

zu üben

zu analysieren. Memory Recycler

Der Speicherkollektor

(wie der in V8) muss in der Lage sein, zu lokalisieren, welche Objekte lebendig sind und welche Objekte, die als tot (Müll) gelten, sind nicht referenzierbar (unrjeweilsfähig). Wenn

Garbage Collection

(GC) aufgrund von Logikfehlern bei der JavaScript-Ausführung keine Müllobjekte sammeln kann, können diese Müllobjekte nicht mehr recycelt werden. Situationen wie diese werden Ihre Anwendung letztendlich immer langsamer machen. Wenn Sie beispielsweise Code schreiben, werden einige

Variablen

und Ereignis-Listener nicht mehr verwendet, einige Codes referenzieren sie jedoch weiterhin. Solange die Referenz noch vorhanden ist, kann das referenzierte Objekt von GC nicht korrekt recycelt werden.

Während Ihre Anwendung ausgeführt wird, wurden möglicherweise einige DOM-Objekte aktualisiert/entfernt. Denken Sie daran, nach Variablen zu suchen, die auf DOM-Objekte verweisen, und diese auf null zu setzen . Überprüfen Sie Objekteigenschaften, die möglicherweise auf andere Objekte (oder andere DOM-Elemente) verweisen. Behalten Sie den Variablen--Cache im Auge, der möglicherweise wächst.

Heap-Analysator

Machen Sie einen Snapshot

Wählen Sie im Profilbereich die Option Take Heap Snapshot und klicken Sie auf Start oder drücken Sie die Befehlstaste + E oder Strg + E:

Der Snapshot wird zunächst im Renderer-Prozessspeicher gespeichert. Sie werden bei Bedarf in DevTools importiert und können angezeigt werden, wenn Sie auf die Schaltfläche „Snapshot“ klicken. Wenn ein Snapshot in DevTools geladen und angezeigt wird, zeigt die Zahl unter dem Snapshot-Titel die Gesamtspeichermenge an, die von erreichbaren JavaScript-Objekten belegt ist.

Beispiel: Probieren Sie die Speicherbereinigung im Beispiel Aktion aus, um die Speichernutzung im Zeitleistenfenster zu überwachen.

Schnappschüsse löschen

Klicken Sie auf das Schaltflächensymbol Alle löschen (), um alle Schnappschüsse zu löschen:

Hinweis: Das Schließen des DevTools-Fensters löscht die gesammelten Snapshots nicht aus dem Rendering-Speicher. Wenn DevTools erneut geöffnet wird, ist die vorherige Snapshot-Liste immer noch vorhanden.

Denken Sie daran, was wir zuvor erwähnt haben: Sie können GC in DevTools erzwingen, wenn Sie einen Snapshot erstellen. Wenn wir einen Schnappschuss machen, wird GC automatisch ausgeführt. Klicken Sie in der Timeline auf die Schaltfläche „Mülleimer“ (Garbage Collection) (), um die Garbage Collection einfach durchzuführen.

Beispiel: Probieren Sie verstreute Objekte aus und analysieren Sie sie mit Heap Profiler. Sie können die Sammlung von (Objekt-)Elementen sehen.

Snapshot-Ansicht wechseln

Ein Snapshot kann je nach Aufgaben die Ansicht wechseln. Sie können durch das Auswahlfeld im Bild wechseln:

Die folgenden drei Standardansichten sind:

  • Zusammenfassung (Zusammenfassung) – Anzeige von Objekten nach Namensklassifizierung durch Konstruktor

  • Containment – kann zum Erkennen von Heap-Inhalten verwendet werden

  • DominatorenDie Ansicht kann im Bereich Einstellungen

    geöffnet werden – zeigt den Dominatorenbaum an. Er kann zum Auffinden von Speicherwachstumspunkten verwendet werden.
Unterscheiden Sie Objekte durch verschiedene Farben

Die Attribute und Attributwerte von Objekten haben unterschiedliche Typen und werden automatisch anhand der Farbe unterschieden. Jede Eigenschaft ist eine der folgenden vier:

a:property

– eine gewöhnliche Eigenschaft, indiziert durch den Namen

von

, dargestellt durch .( dot )
    Operator
  • oder [] (eckige Klammer) Referenz, wie zum Beispiel ["foo bar"];

    0:element - Übergeben Sie gewöhnliche Eigenschaften mit numerischen Indizes, referenziert durch [] (eckige Klammern);

  • a:context var – Eigenschaften innerhalb einer Funktion, innerhalb des Funktionskontexts, durch name Quote;

  • a:system prop – eine Eigenschaft, die von JavaScript VM hinzugefügt wurde und auf die JavaScript-Code nicht zugreifen kann.

  • Das Objekt mit dem Namen

    hat keinen entsprechenden JavaScript-Typ. Sie sind in das JavaScript-VM-Objektsystem integriert. V8 platziert die meisten integrierten Objekte und Benutzer-JS-Objekte im selben Heap. Aber es sind nur die internen Objekte von V8.

  • Details anzeigen

Zusammenfassungsansicht (Zusammenfassungsansicht)SystemÖffnen Sie einen Snapshot, der standardmäßig in der Zusammenfassungsansicht angezeigt wird, die Gesamtzahl der Objekte anzeigt und erweitert werden kann um bestimmte Inhalte anzuzeigen: Zunächst wird in der Zusammenfassungsansicht ein Schnappschuss mit Anzeige

Objektsummen geöffnet, der erweitert werden kann, um Instanzen anzuzeigen:

Die erste Ebene sind die „Gesamt“-Zeilen, sie zeigen:

  • Konstruktor (Konstruktor) bedeutet alle Die Anzahl der Instanzen des von diesem Konstruktor

    generierten Objekts
  • wird in der Spalte Objekte Anzahl

  • angezeigt Die Spalte

    Flache Größe zeigt die Gesamtzahl der flachen Größen (die direkt Speicher belegen) des Objekts, das vom entsprechenden Konstruktor

  • Beibehaltene Größe generiert wurde Die Spalte zeigt den maximalen Speicher, der vom entsprechenden Objekt belegt wird

  • AbstandDie Spalte zeigt den kürzesten Abstand vom Objekt zur GC-Wurzel

Nach dem Erweitern einer Gesamtzeile werden alle Objektinstanzen angezeigt. Der von jeder Instanz belegte direkte Speicher und der insgesamt belegte Speicher werden entsprechend angezeigt. Die Zahl nach dem @-Symbol ist die eindeutige ID des Objekts. Damit können Sie verschiedene Snapshots objektweise vergleichen.

Beispiel: Probieren Sie dieses Beispiel aus (öffnet sich in einem neuen Tab), um zu erfahren, wie Sie die Gliederungsansicht verwenden.

Denken Sie daran, dass gelbe Objekte von JavaScript referenziert werden, während rote Objekte von getrennten Knoten mit gelber Hintergrundfarbe referenziert werden.

Vergleichsansicht

Diese Ansicht wird verwendet, um verschiedene Snapshots zu vergleichen, um die Unterschiede zwischen Snapshots zu ermitteln und um Objekte mit Speicherlecks zu finden. Um zu beweisen, dass ein bestimmter Vorgang der Anwendung keine Lecks verursacht (z. B. führt ein Paar von Vorgängen und Rückgängig-Aktionen, wie das Öffnen und anschließende Schließen eines Dokuments, nicht zu Lecks), können Sie die folgenden Schritte ausprobieren:

  1. Machen Sie vor der Operation einen Heap-Snapshot

  2. Führen Sie eine Operation durch (führen Sie die Aktion aus, von der Sie glauben, dass sie das Leck verursacht);

  3. Machen Sie den vorherigen Vorgang rückgängig (machen Sie den vorherigen Vorgang rückgängig und wiederholen Sie ihn mehrmals);

  4. Machen Sie einen zweiten Schnappschuss und wechseln Sie zur Kontrollansicht mit Schnappschuss 1.

In der Vergleichsansicht werden die Unterschiede zwischen den beiden Schnappschüssen angezeigt. Wenn eine allgemeine Kategorie erweitert wird, werden hinzugefügte und gelöschte Objekte angezeigt:

Beispiel: Probieren Sie das Beispiel aus (in einem neuen Tab öffnen), um zu erfahren, wie Sie es verwenden. Überprüfen Sie die Ansichten auf Lokalisieren Sie Speicherlecks.

Containment-Ansicht

Die Kontrollansicht kann als „Vogelperspektive“ der Objektstruktur Ihrer Anwendung bezeichnet werden. Es ermöglicht Ihnen einen Blick in Funktionen, genau wie Ihre JavaScript-Objekte, in VM-Objekte, wodurch Sie einen sehr detaillierten Überblick über die Speichernutzung in Ihrer Anwendung erhalten.

Diese Ansicht bietet mehrere Einstiegspunkte:

  • DOMWindow-Objekte – Diese Objekte sind „globale“ Objekte für JavaScript-Code; >

  • GC-Stamm

    – der echte GC-Stamm des Garbage Collectors der VM;

  • Natives Objekt

    – Durchsuchen Sie das Objekt wird in die virtuelle JavaScript-Maschine „geschoben“, um automatische Vorgänge auszuführen, z. B.: DOM-Knoten, CSS-Regeln (Details werden im nächsten Abschnitt vorgestellt).

  • Die folgende Abbildung ist a typische Kontrollansicht:

Beispiel: Probieren Sie das Beispiel (in einem neuen Tab öffnen) aus, um zu sehen, wie Sie eine Kontrollansicht verwenden, um in einen

Abschluss

zu sehen und Ereignisbehandlung.

Tipps für Abschlüsse

Die Benennung Ihrer Funktionen kann bei der Unterscheidung zwischen Abschlussfunktionen in Ihrem Snapshot hilfreich sein. Beispiel: Das folgende Beispiel benennt die Funktion nicht:

, während das folgende Beispiel die Funktion benennt:
function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

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

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

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

  return lC;
}

Beispiel: Probieren Sie dieses Beispiel aus, warum eval böse ist, um die Auswirkungen von Schließungen im Speicher zu analysieren. Möglicherweise möchten Sie auch das folgende Beispiel für die Protokollierung von Heap-Zuweisungen ausprobieren.

DOM-Speicherlecks aufdecken

Das Einzigartige an diesem Tool ist, dass es die bidirektionalen Referenzen zwischen browsernativen Objekten (DOM-Knoten, CSS-Regeln) und JavaScript-Objekten anzeigt. Dies kann Ihnen helfen, subtile Speicherlecks zu finden, die dadurch verursacht werden, dass Sie vergessen haben, einen freien untergeordneten DOM-Knoten zu dereferenzieren.

DOM-Speicherlecks können jenseits Ihrer Vorstellungskraft auftreten. Schauen Sie sich das folgende Beispiel an: Wann wird das #tree-Objekt GCed?

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 can be #tree GC
stellt einen Verweis auf seinen übergeordneten Knoten (parentNode) dar, der

rekursiv #leaf auf verweist, sodass nur dann die gesamte Baumstruktur darstellt, wenn leafRef null ist wird von GC recycelt. #tree#tree

Beispiel: Versuchen Sie, DOM-Knoten zu verlieren, um zu verstehen, wo DOM-Knoten Speicher verlieren und wie Sie ihn finden können. Sie können sich auch dieses Beispiel ansehen: DOM-Lecks sind größer als erwartet.

Sehen Sie sich den Artikel „Finden und Debuggen von Speicherlecks mit den Chrome DevTools“ von Gonzalo Ruiz de Villa an, um mehr über DOM-Speicherlecks und die Grundlagen der Speicheranalyse zu erfahren.

Native Objekte sind in den Ansichten „Zusammenfassung“ und „Eindämmung“ leichter zu finden – es gibt spezielle Kategorien für sie:

Beispiel: Probieren Sie dieses Beispiel aus (im neuen Tab). ), um zu lernen, wie man den DOM-Baum trennt.

Dominatoren-Ansicht

Die Dominatoren-Ansicht zeigt den Dominatoren-Baum des Stapeldiagramms an. Die Dominator-Ansicht ähnelt der Containment-Ansicht, verfügt jedoch über keine Eigenschaftsnamen. Dies liegt daran, dass das Lineal möglicherweise ein Objekt ohne direkte Referenz ist, was bedeutet, dass der Linealbaum kein Spanning Tree des Heap-Diagramms ist. Dies ist jedoch eine nützliche Ansicht, die uns dabei helfen kann, Speicherwachstumspunkte schnell zu lokalisieren.

Hinweis: In Chrome Canary kann die Dominator-Ansicht unter „Einstellungen“ > „Erweiterte Heap-Snapshot-Eigenschaften anzeigen“ in DevTools aktiviert werden und wird nach dem Neustart von DevTools wirksam.

Beispiel: Versuchen Sie dieses Beispiel (in einem neuen Tab öffnen), um das Finden von Gedächtniswachstumspunkten zu üben. Sie können das nächste Beispiel zur Beibehaltung von Pfaden und Dominatoren weiter ausprobieren.

Object Allocation Tracker

Object Tracker integriert die inkrementelle Snapshot-Update-Analyse des Heap-Profilers und die Aufzeichnung des Timeline-Panels. Wie bei anderen Tools erfordert das Aufzeichnen der Heap-Konfiguration eines Objekts das Starten der Aufzeichnung, das Durchführen einer Reihe von Vorgängen, das anschließende Stoppen der Aufzeichnung und die Analyse.

Der Objekt-Tracker zeichnet Heap-Snapshots kontinuierlich auf (bis zu alle 50 Millisekunden!) und zeichnet am Ende den letzten Snapshot auf. Der Heap-Zuordnungsprofiler zeigt an, wo ein Objekt erstellt wurde und welchen reservierten Pfad es hat.

Objektanalysator öffnen und verwenden

So beginnen Sie mit der Verwendung von Objektanalysator: 1. Stellen Sie sicher, dass Sie die neueste Version von Chrome verwenden Kanarienvogel.

  1. Öffnen Sie DeveTools und klicken Sie auf das Zahnradsymbol (Übersetzer: Ich verstehe die Verwendung dieses Schritts nicht).

  2. Öffnen Sie nun das Profiler-Panel und Sie sehen die Option „Heap-Zuweisungen aufzeichnen“.

Die Balken oben stellen neue Objekte dar, die im Heap generiert wurden. Die Höhe entspricht der Größe des entsprechenden Objekts und seine Farbe zeigt an, ob das Objekt im letzten aufgenommenen Schnappschuss noch vorhanden ist: Die blaue Spalte zeigt an, dass das Objekt am Ende der Zeitleiste noch vorhanden ist, und die graue Spalte zeigt dies an Das Objekt wurde in der Zeitleiste generiert, aber der Speicher wurde vor dem Beenden zurückgewonnen.

Im obigen Beispiel wird eine Aktion 10 Mal ausgeführt. Das gleiche Programm behält 5 Objekte bei, sodass die letzten 5 blauen Balken erhalten bleiben. Es gibt jedoch potenzielle Probleme mit der letzten verbleibenden Spalte. Sie können den Schieberegler auf der Zeitleiste verwenden, um in diesen bestimmten Schnappschuss hineinzuzoomen und das zugewiesene Objekt zu finden.

Wenn Sie auf ein Objekt im Heap klicken, wird dessen gesamter reservierter Speicherbaum im unteren Teil des Heap-Snapshots angezeigt. Wenn Sie den gesamten reservierten Speicherbaum für dieses Objekt untersuchen, erhalten Sie möglicherweise genügend Informationen, um zu verstehen, warum dieses Objekt nicht erfasst wurde. Anschließend können Sie entsprechende Änderungen an Ihrem Code vornehmen, um unnötige Verweise zu entfernen.

FAQ zur Speicheranalyse

F: Ich kann nicht alle Eigenschaften eines Objekts sehen, ich sehe auch deren Nicht-String-Werte! Warum?

Nicht alle Eigenschaften werden intakt im JavaScript-Heap gespeichert. Einige davon werden durch die Ausführung von Getter-Methoden des nativen Codes abgerufen. Diese Eigenschaften werden nicht im Heap-Snapshot erfasst, um Aufrufe von Gettern zu verhindern und Änderungen am Status des Programms zu vermeiden, wenn diese Getter keine „reinen (Pure)“-Funktionen sind. Ebenso werden Werte, die keine Zeichenfolgen sind, wie etwa Zahlen, nicht erfasst, um die Größe des Snapshots zu reduzieren.

F: Was bedeutet die Zahl nach dem @-Symbol – handelt es sich um eine Adresse oder eine ID? Ist dieser ID-Wert wirklich eindeutig?

Dies ist die Objekt-ID. Die Anzeige der Adresse eines Objekts ist bedeutungslos, da ein Objekt während der Garbage Collection entfernt wird. Bei diesen Objekt-IDs handelt es sich um echte IDs, das heißt, sie werden zwischen verschiedenen Snapshots eindeutig dargestellt. Dies ermöglicht einen genauen Vergleich zwischen Heap-Zuständen. Durch die Beibehaltung dieser IDs entsteht zusätzlicher Overhead für den GC-Prozess, der jedoch nur zugewiesen wird, wenn der erste Heap-Snapshot aufgezeichnet wird. Wenn der Heap-Analysator nicht verwendet wird, entsteht kein zusätzlicher Overhead.

F: Sind „tote“ (nicht referenzierte) Objekte in Snapshots enthalten?

Nein, im Snapshot werden nur Objekte angezeigt, auf die verwiesen werden kann. Darüber hinaus werden GC-Vorgänge automatisch ausgeführt, bevor ein Snapshot erstellt wird.

Hinweis: Zum Zeitpunkt des Schreibens dieses Artikels planen wir, beim Erstellen von Snapshots keine GC mehr durchzuführen, um eine Reduzierung der Heap-Größe zu verhindern. Dies ist nun der Fall, aber die Müllobjekte werden immer noch außerhalb des Snapshots angezeigt.

F: Woraus besteht die GC-Wurzel?

besteht aus vielen Teilen gruppiert in:

  • nativer Objektgraph; > Symbole Tabelle;

  • Stack im VM-Thread

  • Controller-Kontext; ;

  • Globaler Controller.

  • F: Ich habe gelernt, dass ich den Heap-Profiler und die Timeline-Speicheransicht verwenden kann, um Speicherlecks zu erkennen. Aber welches Tool sollte ich zuerst verwenden?

Das Zeitleistenfenster wird verwendet, um eine übermäßige Speichernutzung zu diagnostizieren, wenn Sie Ihre Seite zum ersten Mal verwenden und feststellen, dass sie langsamer wird. Eine langsame Website ist ein klassisches Zeichen für einen Speicherverlust, kann aber auch andere Gründe haben – es könnte ein Rendering- oder Netzwerkengpass vorliegen, also stellen Sie sicher, dass Sie das eigentliche Problem mit Ihrer Seite beheben.

Um festzustellen, ob es sich um ein Speicherproblem handelt, öffnen Sie das Zeitleistenfenster und die Registerkarte „Speicher“. Klicken Sie auf die Schaltfläche „Aufzeichnen“ und wiederholen Sie den Vorgang in Ihrer Anwendung mehrmals, wenn Sie glauben, dass er einen Speicherverlust verursachen könnte. Stoppen Sie die Aufnahme. Das Speichernutzungsdiagramm Ihrer Anwendung wird erstellt. Wenn die Speichernutzung weiter zunimmt (ohne dass es zu einem entsprechenden Rückgang kommt), ist dies ein Zeichen dafür, dass in Ihrer Anwendung möglicherweise ein Speicherverlust vorliegt.

Im Allgemeinen ist das Speichernutzungsdiagramm einer normalen Anwendung gezackt, da der Speicher nach der Verwendung vom Garbage Collector zurückgefordert wird. Machen Sie sich über diesen Zickzack keine Sorgen – durch JavaScript kommt es immer zu Speicherverbrauch, und selbst ein leeres verursacht diesen Zickzack, der unvermeidbar ist. Solange es sich nicht um eine Form handelt, die viel kontinuierlichen Speicher zuweist, bedeutet dies, dass viel Speichermüll erzeugt wird.

Die Wachstumslinie im obigen Bild erfordert Vorsicht. Der DOM-Knotenzähler, der Dokumentzähler und die Anzahl der Ereignis-Listener im Speicher-Tag sind auch bei der Diagnoseanalyse sehr nützlich. Die Anzahl der DOM-Knoten ist der verwendete native Speicher und hat keinen Einfluss auf die JavaScript-Speicherzuordnung.

requestAnimationFrame

Sobald Sie bestätigt haben, dass Ihre Anwendung einen Speicherverlust aufweist, kann ein Heap-Analysator verwendet werden, um den Speicherverlust zu finden.

F: Ich habe festgestellt, dass die Nummern einiger DOM-Knoten im Heap-Snapshot rot als „Getrennter DOM-Baum“ markiert sind, während andere gelb sind. Was bedeutet das?

Sie werden feststellen, dass es verschiedene Farben gibt. Die roten Knoten (mit dunklem Hintergrund) haben keinen direkten Verweis von JavaScript auf sie, sind aber Teil einer separaten DOM-Struktur und bleiben daher im Speicher. Es ist möglich, dass JavaScript auf einen Knoten verweist (vielleicht in einem Abschluss oder einer Variablen) und dieser Verweis verhindert, dass der gesamte DOM-Baum zurückgefordert wird.

Gelbe Knoten (gelber Hintergrund) haben direkte Verweise auf JavaScript. Suchen Sie im selben getrennten DOM-Baum nach einem gelben Knoten, um Ihre JavaScript-Referenzen zu finden. Es ist möglich, die Attributreferenzkette vom DOM-Fenster zu diesem Knoten anzuzeigen (z. B.: ).

Das folgende dynamische Diagramm zeigt den Prozess von getrennten Knoten:

window.foo.bar[2].bazBeispiel

: Probieren Sie dieses Beispiel für getrennte Knoten aus und Sie können das anzeigen Knotenlebenszyklus in der Zeitleiste, und erstellen Sie dann einen Heap-Snapshot, um die getrennten Knoten zu finden.

F: Was stellen die direkte Speicherbelegung (Shallow Size) bzw. die gesamte Speicherbelegung (Retained Size) dar und was ist der Unterschied zwischen ihnen?

In diesem Fall können Objekte auf zwei Arten im Speicher existieren (lebendig sein) – direkt von einem anderen zugänglichen (lebendigen) Objekt beibehalten (Fenster- und Dokumentobjekte sind immer zugänglich) oder implizit enthaltene Referenzen durch native Objekte (wie DOM-Objekte). Die letztere Methode kann zu Speicherverlusten führen, da sie verhindert, dass Objekte automatisch von GC recycelt werden. Der vom Objekt selbst belegte Speicher wird als direkter Speicher bezeichnet (im Allgemeinen reservieren Arrays und Zeichenfolgen mehr direkten Speicher (geringe Größe)).

Ein Objekt beliebiger Größe kann eine große Speichernutzung verhindern, indem es verhindert, dass andere Objekte zurückgefordert werden. Wenn ein Objekt gelöscht wird (einige von ihm erstellte Abhängigkeiten können nicht mehr referenziert werden), wird die Menge an Speicher, die freigegeben werden kann, als insgesamt belegter Speicher (behaltene Größe) bezeichnet.

F: Unter dem Konstruktor und den beibehaltenen Feldern befinden sich viele Daten. Wo sollte ich mit der Untersuchung beginnen, ob bei mir ein Speicherverlust auftritt?

Generell ist es am besten, mit dem ersten Objekt zu beginnen, sortiert nach Retainern. Die Retainer sind nach Abstand sortiert (bezogen auf den Abstand zum Fensterobjekt).

Das Objekt mit der kürzesten Entfernung ist möglicherweise das bevorzugte Objekt, das einen Speicherverlust verursachen kann.

F: Was sind die Unterschiede zwischen den Ansichten Zusammenfassung, Vergleich, Dominatoren und Eindämmung?

Sie können den Unterschied erleben, indem Sie die Ansicht wechseln.

  • Die Zusammenfassungsansicht hilft Ihnen, nach Konstruktoren gruppierte Objekte (und deren Speichernutzung) zu finden. Diese Ansicht ist hilfreich, um DOM-Speicherlecks zu finden.

  • Die Vergleichsansicht kann nach Speicherlecks suchen, indem sie anzeigt, bei welchen Objekten der Speicher korrekt zurückgefordert wurde. Normalerweise werden vor und nach einem Vorgang zwei (oder mehr) Snapshots der Speichernutzung aufgezeichnet. Es prüft, ob ein Speicherverlust vorliegt, indem es die Differenz zwischen dem freigegebenen Speicher und der Anzahl der Referenzen betrachtet, und findet die Ursache.

  • Die Eindämmungsansicht (Steuerung) bietet eine bessere Darstellung der Objektstruktur und hilft uns, die Objektverweise im globalen Bereich (z. B. Fenster) zu analysieren, um herauszufinden, was diese Objekte behält. Es ermöglicht Ihnen, Abschlüsse zu analysieren und tiefer in Objekte vorzudringen.

  • Die Dominators-Ansicht kann verwendet werden, um uns dabei zu helfen, zu bestätigen, dass an einem bestimmten Ort (z. B. die, auf die verwiesen wird) noch keine redundanten Objekte hängen (z. B. diejenigen, auf die verwiesen wird), und um das Löschen/Papierkorb von zu bestätigen Objekte Recycling macht wirklich einen Unterschied.

F: Was stellt der Inhalt des Konstruktors (einer Gruppe) im Heap-Analysator dar?

  • (globale Eigenschaft) – zwischen einem globalen Objekt (wie „Fenster“) und Objekten, die darauf verweisen Zwischenobjekt. Wenn ein Objekt vom Konstruktor Person generiert und vom globalen Objekt referenziert wird, lautet der Referenzpfad wie folgt: [global] > (globale Eigenschaft) > Dies unterscheidet sich von Objekten, die direkt aufeinander verweisen. Aus Leistungsgründen verwenden wir Zwischenobjekte. Globale Objekte ändern sich häufig, und die Optimierung des Attributzugriffs nicht globaler Variablen gilt nicht für globale Variablen.

  • (roots) – Der Inhalt von Roots im Konstruktor bezieht sich auf das ausgewählte Objekt. Es können auch Referenzen sein, die von der Engine autonom erstellt werden. Diese Engine verfügt über einen Cache für referenzierte Objekte, aber diese Referenzen verhindern nicht, dass das referenzierte Objekt wiederverwendet wird, sodass es sich nicht um wirklich starke Referenzen (FIXME) handelt.

  • (Abschluss) – ein Verweis auf eine Reihe von Objekten in einem Funktionsabschluss

  • (array, string, number, regexp) – Eine Reihe von Eigenschaften, die auf einen Objekttyp Array, String, Number oder regulären Ausdruck

  • (kompilierter Code) – Einfach ausgedrückt hängt alles mit kompiliertem Code zusammen. Script ist wie eine Funktion, entspricht aber tatsächlich dem Inhalt von 3f1c4e4b6b16bbbd69b2ee476dc4f83a. SharedFunctionInfos (SFI) sind Objekte zwischen Funktionen und kompiliertem Code. Funktionen haben normalerweise einen Inhalt, SFIS nicht (FIXME).

  • HTMLpElement, HTMLAnchorElement, DocumentFragment usw. – Verweise auf Elemente oder Dokumentobjekte in Ihrem Code.

Viele andere Objekte, die während des Lebenszyklus Ihres Programms generiert werden, einschließlich Ereignis-Listener oder benutzerdefinierte Objekte, können in den folgenden Controllern gefunden werden:

F: Muss ich Funktionen in Chrome deaktivieren, die Auswirkungen auf die Speicheranalyse haben könnten?

Wir empfehlen, dass Sie bei der Verwendung von Chrome DevTools für die Speicheranalyse den Inkognito-Modus mit deaktivierten Erweiterungen verwenden oder den Benutzerordner auf (

) setzen, bevor Sie Chrome öffnen. --user-data-dir=""

Anwendungen, Erweiterungen und sogar Datensätze in der Konsole haben potenzielle Auswirkungen auf Ihre Analyse. Wenn Sie möchten, dass Ihre Analyse zuverlässig ist, deaktivieren Sie diese.

Schreiben Sie die Wörter am Ende

Heutige JavaScript-Engines verfügen bereits über starke Fähigkeiten und können den durch den Code erzeugten Speichermüll automatisch recyceln. Das heißt, sie können nur bis zu einem gewissen Grad gehen, aber unsere Anwendung verursacht nachweislich immer noch Speicherverluste aufgrund logischer Fehler. Verwenden Sie die entsprechenden Tools, um die Engpässe Ihrer Anwendung zu finden, und denken Sie daran: Raten Sie nicht, sondern testen Sie es.

Hilfebeispiele

Diagnose von Speicherlecks

Obwohl in diesem Artikel viel behandelt wurde, ist eine Reihe von Beispielen zum Testen von speicherbezogenen Problemen dennoch nützlich. Nachfolgend finden Sie eine Reihe davon von Beispielen für Speicherlecks im DOM-Knoten. Möglicherweise möchten Sie mit diesen Beispielen experimentieren, bevor Sie Ihre komplexeren Seiten oder Anwendungen testen.

  • Beispiel 1: Wachsendes Gedächtnis

  • Beispiel 2: Garbage Collection in Aktion

  • Beispiel 3 : Verstreute Objekte

  • Beispiel 4: Freistehende Knoten

  • Beispiel 5: Speicher und versteckte Klassenes

  • Beispiel 6: Undichte DOM-Knoten

  • Beispiel 7: Eval ist böse (fast immer)

  • Beispiel 8 : Heap-Zuweisungen aufzeichnen

  • Beispiel 9: DOM-Lecks größer als erwartet

  • Beispiel 10: Pfad beibehalten

  • Beispiel 11: Letzte Übung

Das obige ist der detaillierte Inhalt vonGrafische Einführung in die JavaScript-Speicheranalyse in den Chrome Developer Tools. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn