Get to know MDN better
Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.
Niedrig-levelige Sprachen wie C verfügen über manuelle Speicherverwaltungs-Primitiven wie malloc() und free(). Im Gegensatz dazu allociert JavaScript automatisch Speicher, wenn Objekte erstellt werden, und gibt ihn frei, wenn sie nicht mehr verwendet werden (Garbage Collection). Diese Automatik kann eine potenzielle Quelle der Verwirrung sein: Sie kann Entwicklern den falschen Eindruck vermitteln, sich nicht um die Speicherverwaltung kümmern zu müssen.
Unabhängig von der Programmiersprache ist der Speicherlebenszyklus praktisch immer derselbe:
Der zweite Teil ist in allen Sprachen explizit. Die ersten und letzten Teile sind in niedrig-leveligen Sprachen explizit, aber in hoch-leveligen Sprachen wie JavaScript meist implizit.
Um den Programmierer nicht mit Allokationen zu belasten, allociert JavaScript automatisch Speicher, wenn Werte initial deklariert werden.
Einige Funktionsaufrufe führen zur Allokation von Objekten.
Einige Methoden allocieren neue Werte oder Objekte:
Die Verwendung von Werten bedeutet im Wesentlichen das Lesen und Schreiben im allozierten Speicher. Dies kann durch Lesen oder Schreiben des Wertes einer Variablen oder einer Objekteigenschaft oder sogar durch Übergeben eines Arguments an eine Funktion erfolgen.
Die meisten Speicherverwaltungsprobleme treten in dieser Phase auf. Der schwierigste Aspekt dieser Phase ist es zu bestimmen, wann der allozierte Speicher nicht mehr benötigt wird.
Niedrig-levelige Sprachen erfordern vom Entwickler, manuell zu bestimmen, an welchem Punkt im Programm der allozierte Speicher nicht mehr benötigt wird, und diesen freizugeben.
Einige hoch-levelige Sprachen wie JavaScript verwenden eine Form der automatischen Speicherverwaltung, die als Garbage Collection (GC) bekannt ist. Ziel eines Garbage Collectors ist es, die Speicherallokation zu überwachen und festzustellen, wann ein Block allozierter Speicher nicht mehr benötigt wird, und diesen freizugeben. Dieser automatische Prozess ist eine Annäherung, da das allgemeine Problem der Bestimmung, ob ein bestimmtes Stück Speicher noch benötigt wird, unentscheidbar ist.
Wie oben erwähnt, ist das allgemeine Problem, automatisch zu finden, ob ein Speicher "nicht mehr benötigt wird", unentscheidbar. Daher implementieren Garbage Collector eine eingeschränkte Lösung für das allgemeine Problem. Dieser Abschnitt erklärt die Konzepte, die zum Verständnis der wichtigsten Garbage-Collection-Algorithmen und ihrer jeweiligen Einschränkungen erforderlich sind.
Das Hauptkonzept, auf das sich Garbage-Collection-Algorithmen stützen, ist das Konzept der Referenz. Im Kontext der Speicherverwaltung wird gesagt, dass ein Objekt ein anderes Objekt referenziert, wenn das erstgenannte Zugang zum letzteren hat (entweder implizit oder explizit). Beispielsweise hat ein JavaScript-Objekt eine Referenz auf sein Prototype (implizite Referenz) und auf die Werte seiner Eigenschaften (explizite Referenz).
In diesem Kontext wird der Begriff "Objekt" auf etwas erweitert, das breiter ist als reguläre JavaScript-Objekte und enthält auch Funktionsumgebungen (oder die globale lexikalische Umgebung).
Hinweis: Keine moderne JavaScript-Engine verwendet mehr Referenzzählung für Garbage Collection.
Dies ist der naivste Garbage Collection Algorithmus. Dieser Algorithmus reduziert das Problem von der Bestimmung, ob ein Objekt noch benötigt wird, auf die Bestimmung, ob ein Objekt noch von anderen Objekten referenziert wird. Ein Objekt wird als "Garbage" oder sammelbar betrachtet, wenn es keine Referenzen mehr darauf gibt.
Zum Beispiel:
Es gibt eine Einschränkung im Hinblick auf zirkuläre Referenzen. Im folgenden Beispiel werden zwei Objekte mit Eigenschaften erstellt, die sich gegenseitig referenzieren, wodurch ein Zyklus entsteht. Sie gehen außer Sichtweite, nachdem der Funktionsaufruf abgeschlossen ist. Zu diesem Zeitpunkt werden sie überflüssig und ihr allozierter Speicher sollte freigegeben werden. Der Referenzzählungsalgorithmus wird sie jedoch nicht als sammelbar betrachten, da jedes der beiden Objekte mindestens eine Referenz hat, die darauf zeigt, was dazu führt, dass keines von ihnen zur Garbage Collection markiert wird. Zirkuläre Referenzen sind eine häufige Ursache für Speicherlecks.
Dieser Algorithmus reduziert die Definition von "ein Objekt wird nicht mehr benötigt" auf "ein Objekt ist nicht erreichbar".
Dieser Algorithmus setzt die Kenntnis einer Menge von Objekten voraus, die als Wurzeln bezeichnet werden. In JavaScript ist die Wurzel das globale Objekt. Der Garbage Collector wird regelmäßig von diesen Wurzeln aus starten, alle Objekte finden, die von diesen Wurzeln referenziert werden, dann alle von diesen referenzierten Objekte finden usw. Beginnend bei den Wurzeln findet der Garbage Collector somit alle erreichbaren Objekte und sammelt alle nicht erreichbaren Objekte ein.
Dieser Algorithmus stellt eine Verbesserung gegenüber dem vorherigen dar, da ein Objekt mit null Referenzen effektiv nicht erreichbar ist. Das Gegenteil gilt jedoch nicht, wie wir an zirkulären Referenzen gesehen haben.
Derzeit beinhalten alle modernen Engines einen Mark-and-Sweep-Garbage-Collector. Alle Verbesserungen, die im Bereich der JavaScript-Garbage-Collection (generational/incremental/concurrent/parallel Garbage Collection) in den letzten Jahren gemacht wurden, sind Implementierungsverbesserungen dieses Algorithmus, aber keine Verbesserungen des Garbage-Collection-Algorithmus selbst noch seiner Reduzierung der Definition, wann "ein Objekt nicht mehr benötigt wird".
Der unmittelbare Vorteil dieses Ansatzes ist, dass Zyklen kein Problem mehr darstellen. Im ersten obigen Beispiel sind nach Rückgabe des Funktionsaufrufs die beiden Objekte nicht mehr von einer Ressource referenziert, die vom globalen Objekt erreichbar ist. Folglich werden sie vom Garbage Collector als nicht erreichbar eingestuft und ihr allozierter Speicher wird freigegeben.
Jedoch bleibt die Unfähigkeit, die Garbage Collection manuell zu steuern. Es gibt Zeiten, in denen es praktisch wäre, manuell zu entscheiden, wann und welcher Speicher freigegeben wird. Um den Speicher eines Objekts freizugeben, muss es explizit nicht erreichbar gemacht werden. Es ist auch nicht möglich, programmatisch die Garbage Collection in JavaScript zu triggern – und wird wahrscheinlich nie innerhalb der Kernsprache möglich sein, obwohl Engines APIs hinter Opt-in-Flags aussetzen können.
JavaScript-Engines bieten typischerweise Flags an, die das Speicher-Modell freilegen. Zum Beispiel bietet Node.js zusätzliche Optionen und Werkzeuge, die die zugrunde liegenden V8-Mechanismen zur Konfiguration und Debugging von Speicherproblemen freigeben. Diese Konfiguration ist in Browsern möglicherweise nicht verfügbar, und noch weniger für Webseiten (via HTTP-Header usw.).
Die maximale Menge des verfügbaren Heap-Speichers kann mit einem Flag erhöht werden:
Wir können auch den Garbage Collector für das Debugging von Speicherproblemen mit einem Flag und dem Chrome-Debugger freilegen:
Obwohl JavaScript die Garbage Collector API nicht direkt freilegt, bietet die Sprache mehrere Datenstrukturen an, die die Garbage Collection indirekt beobachten und zur Verwaltung der Speichernutzung eingesetzt werden können.
WeakMap und WeakSet sind Datenstrukturen, deren APIs ihren nicht-weak Gegenstücken: Map und Set eng nachempfunden sind. WeakMap erlaubt es Ihnen, eine Sammlung von Schlüssel-Wert-Paaren zu verwalten, während WeakSet Ihnen erlaubt, eine Sammlung eindeutiger Werte zu verwalten, beide mit performanten Hinzufügen, Löschen und Abfragen.
WeakMap und WeakSet haben ihren Namen vom Konzept der schwach gehaltenen Werte. Wenn x von y schwach gehalten wird, bedeutet das, dass Sie den Wert von x zwar über y erreichen können, der Mark-and-Sweep-Algorithmus x jedoch nicht als erreichbar betrachtet, wenn sonst nichts stark an ihm festhält. Die meisten Datenstrukturen, außer den hier besprochenen, halten die ihnen übergebenen Objekte fest, sodass Sie sie jederzeit abrufen können. Die Schlüssel von WeakMap und WeakSet können garbage-getrennt werden (bei WeakMap-Objekten wären die Werte dann ebenfalls für die Garbage Collection geeignet), solange nichts anderes im Programm den Schlüssel referenziert. Dies wird durch zwei Eigenschaften sichergestellt:
In typischen Erklärungen von WeakMap und WeakSet (wie der obenstehenden) wird oft impliziert, dass der Schlüssel zuerst garbage-getrennt wird, wodurch auch der Wert zur Garbage Collection freigegeben wird. Betrachten Sie jedoch den Fall, dass der Wert den Schlüssel referenziert:
Wenn key als tatsächliche Referenz gespeichert wird, würde dies eine zirkuläre Referenz erzeugen und sowohl den Schlüssel als auch den Wert unbrauchbar für die Garbage Collection machen, selbst wenn nichts anderes key referenziert – denn wenn key garbage-getrennt wird, bedeutet das, dass zu einem bestimmten Zeitpunkt value.key auf eine nicht existierende Adresse zeigen würde, was nicht zulässig ist. Um dies zu beheben, sind die Einträge von WeakMap und WeakSet keine tatsächlichen Referenzen, sondern Ephemerons, eine Erweiterung des Mark-and-Sweep-Mechanismus. Barros et al. bieten eine gute Zusammenfassung des Algorithmus (Seite 4). Um einen Abschnitt zu zitieren:
Ephemerons sind eine Verfeinerung von schwachen Paaren, bei denen weder der Schlüssel noch der Wert als schwach oder stark klassifiziert werden können. Die Konnektivität des Schlüssels bestimmt die Konnektivität des Wertes, aber die Konnektivität des Wertes beeinflusst nicht die Konnektivität des Schlüssels. […] wenn die Garbage Collection Unterstützung für Ephemerons bietet, erfolgt sie in drei Phasen statt in zwei (Mark und Sweep).
Als grobes mentales Modell denken Sie an eine WeakMap als folgende Implementierung:
Warnung: Dies ist kein Polyfill, noch ist es auch nur annähernd so, wie es in der Engine implementiert ist (die in den Garbage Collection Mechanismus eingreift).
Wie Sie sehen, hält das MyWeakMap nie tatsächlich eine Sammlung von Schlüsseln. Es fügt jedem Objekt, das eingefügt wird, einfach Metadaten hinzu. Das Objekt ist dann über Mark-and-Sweep für die Garbage Collection geeignet. Daher ist es nicht möglich, über die Schlüssel in einer WeakMap zu iterieren noch die WeakMap zu leeren (da dies ebenfalls auf das Wissen über die gesamte Schlüsselsammlung angewiesen wäre).
Für weitere Informationen zu ihren APIs, siehe den Leitfaden zu den zugeordneten Sammlungen.
Hinweis: WeakRef und FinalizationRegistry bieten direkten Einblick in die Garbage Collection Mechanismen. Vermeiden Sie die Nutzung, wo möglich, da die Laufzeitsemantik fast vollständig ungarantiert ist.
Alle Variablen mit einem Objekt als Wert sind Referenzen auf dieses Objekt. Solche Referenzen sind jedoch stark – ihre Existenz verhindert, dass der Garbage Collector das Objekt als für die Sammlung geeignet markiert. Ein WeakRef ist eine schwache Referenz zu einem Objekt, die es ermöglicht, dass das Objekt garbage-getrennt wird, während die Möglichkeit besteht, den Inhalt des Objekts während seiner Lebenszeit zu lesen.
Ein Anwendungsfall für WeakRef ist ein Cache-System, das string URLs großen Objekten zuordnet. Wir können hierfür keine WeakMap verwenden, da WeakMap-Objekte ihre Schlüssel schwach gehalten, aber nicht ihre Werte – wenn Sie auf einen Schlüssel zugreifen, erhalten Sie deterministisch immer den Wert (da der Zugriff auf den Schlüssel bedeutet, dass er noch lebt). Hier ist es in Ordnung, undefined für einen Schlüssel zu erhalten (wenn der entsprechende Wert nicht mehr lebendig ist), da wir ihn einfach neu berechnen können, aber wir wollen keine unerreichbaren Objekte im Cache behalten. In diesem Fall können wir eine normale Map verwenden, aber mit jedem Wert als WeakRef des Objekts anstelle des tatsächlichen Objektwerts.
FinalizationRegistry bietet einen noch stärkeren Mechanismus, um die Garbage Collection zu beobachten. Er ermöglicht es Ihnen, Objekte zu registrieren und benachrichtigt zu werden, wenn sie garbage-getrennt werden. Zum Beispiel, für das oben beschriebene Cache-System, selbst wenn die Blobs selbst für die Sammlung freigegeben sind, sind die WeakRef-Objekte, die sie enthalten, nicht – und im Laufe der Zeit kann die Map viele nutzlose Einträge ansammeln. Mit einem FinalizationRegistry kann man in diesem Fall Aufräumarbeiten durchführen.
Aufgrund von Leistungs- und Sicherheitsbedenken gibt es keine Garantie, wann der Callback aufgerufen wird oder ob er überhaupt aufgerufen wird. Er sollte nur für Aufräumarbeiten – und nicht kritische Aufräumarbeiten – verwendet werden. Es gibt andere Möglichkeiten für bestimmtere Ressourcenverwaltung, wie try...finally, das den finally-Block immer ausführen wird. WeakRef und FinalizationRegistry existieren ausschließlich zur Optimierung der Speichernutzung in langlaufenden Programmen.
Für weitere Informationen über die API von WeakRef und FinalizationRegistry, siehe deren Referenzseiten.
Der Bauplan für ein besseres Internet.
Besuche die gemeinnützige Muttergesellschaft der Mozilla Corporation, die Mozilla Foundation.
Teile dieses Inhalts sind ©1998–2026 von einzelnen mozilla.org-Mitwirkenden. Inhalte sind verfügbar unter einer Creative-Commons-Lizenz.