Einführung
Low-Level-Sprachen wie C verfügen über Low-Level-Speicherverwaltungsprimitive wie malloc() und free(). Die Speicherprimitive von JavaScript hingegen werden beim Erstellen von Variablen (Objekten, Zeichenfolgen usw.) zugewiesen und dann „automatisch“ freigegeben, wenn sie nicht mehr verwendet werden. Letzteres wird Garbage Collection genannt. Diese „Automatik“ ist verwirrend und vermittelt den Entwicklern von JavaScript (und anderen Hochsprachen) die Illusion, dass sie sich keine Gedanken über die Speicherverwaltung machen müssen.
Speicherlebenszyklus
Unabhängig von der Programmiersprache ist der Speicherlebenszyklus grundsätzlich derselbe:
1. Weisen Sie den Speicher zu, den Sie benötigen
2. Benutze es (lesen, schreiben)
3. Lassen Sie es los, wenn es nicht verwendet wird. PS: Es hat die gleiche Bedeutung wie „einen Elefanten in den Kühlschrank stellen“
Der erste und zweite Teil des Prozesses sind in allen Sprachen klar. Der letzte Schritt ist in Low-Level-Sprachen klar, aber in High-Level-Sprachen wie JavaScript ist der letzte Schritt nicht klar.
JavaScript-Speicherzuweisung
Variableninitialisierung
Um Programmierern keine Sorgen über die Zuweisung zu machen, schließt JavaScript die Speicherzuweisung bei der Definition von Variablen ab.
var o = {
a: 1,
b: null
}; // Speicher für das Objekt und die darin enthaltenen Variablen zuweisen
var a = [1, null, "abra"]; // Speicher für das Array und die darin enthaltenen Variablen zuweisen (genau wie Objekte)
Funktion f(a){
gib eine 2 zurück;
} // Speicher für die Funktion (aufrufbares Objekt) reservieren
// Funktionsausdrücke können auch ein Objekt zuordnen
someElement.addEventListener('click', function(){
someElement.style.backgroundColor = 'blue';
}, false);
Speicherzuweisung per Funktionsaufruf
Einige Funktionsaufrufe führen zu einer Objektspeicherzuweisung:
Einige Methoden weisen neue Variablen oder neue Objekte zu:
var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // Es gibt vier Elemente im neuen Array, die Array a und Array a2 verbinden.
Verwendung von Werten
Der Prozess der Verwendung von Werten ist eigentlich ein Vorgang zum Lesen und Schreiben des zugewiesenen Speichers, was bedeutet, dass Sie eine Variable oder den Attributwert eines Objekts schreiben und sogar Funktionsparameter übergeben können.
Speicher freigeben, wenn er nicht mehr benötigt wird
Die meisten Speicherverwaltungsprobleme treten in dieser Phase auf. Die schwierigste Aufgabe besteht hier darin, festzustellen, dass der zugewiesene Speicher tatsächlich nicht mehr benötigt wird. Oft muss der Entwickler ermitteln, welcher Teil des Programmspeichers nicht mehr benötigt wird, und ihn freigeben.
Der Hochspracheninterpreter bettet einen „Garbage Collector“ ein, dessen Hauptaufgabe darin besteht, die Zuweisung und Nutzung von Speicher zu verfolgen, sodass dieser automatisch freigegeben werden kann, wenn der zugewiesene Speicher nicht mehr verwendet wird. Dieser Prozess ist eine Näherung, da nicht zu entscheiden ist, ob ein bestimmtes Stück Speicher benötigt wird (kann nicht durch einen bestimmten Algorithmus gelöst werden
).Müllabfuhr
Wie oben erwähnt, kann das Problem der automatischen Feststellung, ob ein Teil des Speichers „nicht mehr benötigt“ wird, nicht bestimmt werden. Daher können Garbage-Collection-Implementierungen allgemeine Probleme nur bedingt lösen. In diesem Abschnitt werden die Konzepte erläutert, die zum Verständnis der wichtigsten Garbage-Collection-Algorithmen und ihrer Einschränkungen erforderlich sind.
Zitat
Garbage-Collection-Algorithmen basieren hauptsächlich auf dem Konzept der Referenzen. Wenn ein Objekt im Kontext der Speicherverwaltung die Berechtigung hat, auf ein anderes Objekt zuzugreifen (implizit oder explizit), wird es als Objekt bezeichnet, das auf ein anderes Objekt verweist. Beispielsweise verfügt ein Javascript-Objekt über einen Verweis auf seinen Prototyp (einen impliziten Verweis) und einen Verweis auf seine Eigenschaften (einen expliziten Verweis).
Hier ist das Konzept „Objekt“ nicht nur ein spezielles Javascript-Objekt, sondern umfasst auch den Funktionsbereich (oder den globalen lexikalischen Bereich).
Referenzzählung Garbage Collection
Dies ist der einfachste Garbage-Collection-Algorithmus. Dieser Algorithmus vereinfacht die Definition von „ob das Objekt nicht mehr benötigt wird“ als „ob das Objekt von anderen Objekten referenziert wird“. Wenn keine Referenzen auf das Objekt vorhanden sind (null Referenzen), wird das Objekt vom Garbage-Collection-Mechanismus zurückgefordert.
Zum Beispiel
var o2 = o; // Die o2-Variable ist die zweite Referenz auf „dieses Objekt“
o = 1; // Jetzt wird die ursprüngliche Referenz o von „diesem Objekt“ durch o2
var oa = o2.a; // Referenzieren Sie die a-Eigenschaft von „diesem Objekt“
// Nun hat „dieses Objekt“ zwei Referenzen, eine ist o2 und die andere ist oa
o2 = "yo"; // Das Originalobjekt hat jetzt keine Referenzen
// Er kann im Müll gesammelt werden
// Das Objekt seines Attributs a wird jedoch immer noch von oa referenziert, sodass es noch nicht recycelt werden kann
oa = null; // Das Objekt mit dem a-Attribut hat jetzt auch Null-Referenz
// Es kann im Müll gesammelt werden
Einschränkung: Zirkelverweis
Eine Einschränkung dieses einfachen Algorithmus besteht darin, dass, wenn ein Objekt auf ein anderes verweist (und einen Zirkelverweis bildet), diese möglicherweise „nicht mehr benötigt“ werden, aber nicht recycelt werden.
return „azerty“;
}
f();
// Zwei Objekte werden erstellt und verweisen aufeinander und bilden eine Schleife
// Sie verlassen den Funktionsumfang nicht, nachdem sie
aufgerufen wurden
// Sie sind also nicht mehr nützlich und können recycelt werden
// Der Referenzzählalgorithmus berücksichtigt jedoch, dass sie alle mindestens eine Referenz aufeinander haben, sodass sie nicht recycelt werden
Praxisbeispiele
IE 6, 7 führt das Referenzzähl-Recycling für DOM-Objekte durch. Ein häufiges Problem für sie sind Speicherlecks:
Mark-and-Clear-Algorithmus
Dieser Algorithmus vereinfacht die Definition von „ob das Objekt nicht mehr benötigt wird“ als „ob das Objekt erhalten werden kann“.
Dieser Algorithmus geht davon aus, dass ein Objekt namens root eingerichtet wird (in Javascript ist root das globale Objekt). In regelmäßigen Abständen beginnt der Garbage Collector im Stammverzeichnis, findet alle Objekte, auf die vom Stammverzeichnis aus verwiesen wird, und findet dann die Objekte, auf die diese Objekte verweisen ... Ausgehend vom Stammverzeichnis findet der Garbage Collector alle Objekte, die abgerufen werden können, und alle Objekte das kann man nicht bekommen.
Dieser Algorithmus ist besser als der vorherige, da „Objekte mit Nullreferenz“ immer nicht erhältlich sind, aber das Gegenteil ist nicht unbedingt der Fall, siehe „zyklische Referenz“.
Seit 2012 verwenden alle modernen Browser den Mark-and-Sweep-Garbage-Collection-Algorithmus. Alle Verbesserungen am JavaScript-Garbage-Collection-Algorithmus basieren auf Verbesserungen am Mark-Sweep-Algorithmus und verbessern nicht den Mark-Sweep-Algorithmus selbst und seine vereinfachte Definition, „ob ein Objekt nicht mehr benötigt wird“.
Zirkelverweise sind kein Problem mehr
Im obigen Beispiel können die beiden Objekte nach der Rückkehr des Funktionsaufrufs nicht vom globalen Objekt abgerufen werden. Daher werden sie vom Garbage Collector eingesammelt.
Im zweiten Beispiel werden das Div und seine Event-Handler, sobald sie vom Stammverzeichnis aus nicht erreichbar sind, vom Garbage Collector gesammelt.
Einschränkungen: Objekte müssen explizit nicht erreichbar sein
Obwohl dies eine Einschränkung darstellt, wird sie selten überschritten, weshalb sich in Wirklichkeit nur wenige Menschen für den Garbage-Collection-Mechanismus interessieren.