Vorwort
Bevor wir über das Problem der Speicherlecks sprechen, werfen wir einen Blick auf den Garbage-Collection-Mechanismus von JavaScript. JavaScript verfügt über einen automatischen Garbage-Collection-Mechanismus, der darin besteht, die Variablen zu finden, die nicht mehr verwendet werden Geben Sie die Erinnerung frei, die sie belegen. Zu diesem Zweck arbeitet der Garbage Collector in festen Intervallen (oder geplanten Sammelzeiten während der Codeausführung). Es gibt zwei häufig verwendete Methoden: klare Markierung und Referenzzählung.
1. Mark Sweep
Die am häufigsten verwendete Garbage-Collection-Methode in JavaScript ist Mark-and-Sweep. Der Garbage Collector markiert bei seiner Ausführung alle im Speicher gespeicherten Variablen (jede Markierungsmethode kann verwendet werden). Anschließend werden die Tags von Variablen in der Umgebung und Variablen, auf die Variablen in der Umgebung verweisen, entfernt. Variablen, die danach markiert werden, werden als zu löschende Variablen betrachtet, da Variablen in der Umgebung nicht mehr auf diese Variablen zugreifen können. Schließlich führt der Garbage Collector die Speicherbereinigung durch, indem er die markierten Werte zerstört und den von ihnen belegten Speicherplatz zurückgewinnt.
2. Referenzzählung
Die Bedeutung der Referenzzählung besteht darin, die Anzahl der Referenzen auf jeden Wert zu verfolgen und aufzuzeichnen. Wenn eine Variable deklariert und der Variablen ein Referenztypwert zugewiesen wird, beträgt die Anzahl der Referenzen auf diesen Wert 1. Wenn derselbe Wert einer anderen Variablen zugewiesen wird, wird der Referenzzähler des Werts um 1 erhöht. Wenn umgekehrt die Variable, die einen Verweis auf diesen Wert enthält, einen anderen Wert erhält, wird die Anzahl der Verweise auf diesen Wert um eins dekrementiert. Wenn die Anzahl der Verweise auf diesen Wert 0 wird, bedeutet dies, dass es keine Möglichkeit mehr gibt, auf diesen Wert zuzugreifen, sodass der von ihm belegte Speicherplatz zurückgewonnen werden kann. Auf diese Weise gibt der Garbage Collector bei der nächsten Ausführung den Speicher frei, der von Werten ohne Referenzen belegt ist.
Netscape Navigator 3.0 war der erste Browser, der die Referenzzählstrategie verwendete, aber bald stieß er auf ein ernstes Problem, siehe folgendes Beispiel:
function problem(){ var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA; }
Erklärung: ObjektA und ObjektB beziehen sich auf einander durch ihre jeweiligen Attribute, d. h. die Anzahl der Referenzen beider Objekte beträgt 2. Bei der Implementierung mit der Mark-and-Sweep-Strategie ist diese gegenseitige Referenz keine Referenz, da beide Objekte nach Ausführung der Funktion den Gültigkeitsbereich verlassen Frage. In einer Implementierung, die eine Referenzzählstrategie verwendet, bleiben ObjektA und ObjektB jedoch nach Ausführung der Funktion weiterhin bestehen, da ihre Anzahl an Referenzen niemals 0 sein wird. Wenn diese Funktion mehrmals aufgerufen wird, wird eine große Menge an Speicher nicht wiederverwendet.
Aus diesem Grund hat Netscape die Referenzzählmethode in Navigator 4.0 aufgegeben. Damit waren die durch die Referenzzählung verursachten Probleme jedoch noch nicht beendet. Einige Objekte im IE vor 9 waren keine nativen JavaScript-Objekte. Beispielsweise werden die Objekte in BOM und DOM mithilfe von C in Form von COM-Objekten (Component Object Model) implementiert, und der Garbage Collection-Mechanismus von COM-Objekten verwendet eine Referenzzählstrategie. Obwohl die JavaScript-Engine des IE mit der Mark-and-Sweep-Strategie implementiert ist, basieren die COM-Objekte, auf die JavaScript zugreift, immer noch auf der Referenzzählstrategie. Mit anderen Worten: Immer wenn COM-Objekte im IE beteiligt sind, entsteht ein Zirkelverweisproblem.
Zum Beispiel:
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.element = element; element.someObject = myObject;
Ein Zirkelverweis wird zwischen einem DOM-Element (element) und einem nativen JavaScript-Objekt (myObject) erstellt. Darunter verfügt die Variable myObject über eine Eigenschaft namens „element“, die auf das Elementobjekt verweist, und die Variable „element“ verfügt außerdem über eine Eigenschaft namens „someObject“, die auf „myObject“ verweist. Aufgrund dieses Zirkelverweises wird das DOM im Beispiel selbst dann nie wiederverwendet, wenn es von der Seite entfernt wird.
Lösung: Setzen Sie die Variable auf null, um die Verbindung zwischen der Variablen und dem Wert, auf den sie zuvor verwiesen hat, zu trennen.
myObject.element = null; element.someObject = null;
Nachdem ich das oben Gesagte gelesen habe, möchte ich über das Thema sprechen.
Schließungen verursachen keine Speicherlecks
Da Versionen vor IE9 unterschiedliche Garbage Collection für JScript-Objekte und COM-Objekte verwenden. Schließungen verursachen daher in diesen IE-Versionen einige besondere Probleme. Wenn ein HTML-Element in der Gültigkeitskette des Abschlusses gespeichert ist, bedeutet dies insbesondere, dass das Element nicht zerstört werden kann. Bitte sehen Sie sich das Beispiel an:
function assignHandler(){ var element = document.getElementById("someElement"); element.onclick = function(){ alert(element.id); }; }
Der obige Code erstellt ein Elementereignis als Element Der Abschluss des Handlers, der wiederum einen Zirkelverweis erstellt. Da die anonyme Funktion einen Verweis auf das aktive Objekt von „assignHandler()“ speichert, führt dies dazu, dass die Anzahl der Verweise auf das Element nicht reduziert werden kann. Solange die anonyme Funktion existiert, beträgt die Referenznummer des Elements mindestens 1, sodass der von ihm belegte Speicher niemals recycelt wird
Lösung Speichern Sie wie im Vorwort erwähnt eine Kopie von element.id in einer Variablen , wodurch der Zirkelverweis auf die Variable im Abschluss eliminiert und die Elementvariable auf null gesetzt wird.
function assignHandler(){ var element = document.getElementById("someElement"); var id = element.id; element.onclick = function(){ alert(id); }; element = null; }
Zusammenfassung: Schließungen verursachen keine Speicherverluste, aber da Versionen vor IE9 unterschiedliche Garbage Collection für JScript-Objekte und COM-Objekte verwenden, ist dies ein Problem mit IE, also Schließungen haben nichts mit Speicherlecks zu tun.