28

Betrachten Sie dieses Zitat von the Mozilla Docs on JavaScript memory leaks:DOM: Warum ist das ein Speicherleck?

function addHandler() { 
    var el = document.getElementById('el'); 
    el.onclick = function() { 
     this.style.backgroundColor = 'red'; 
    } 
} 

Der obige Code setzt das Element rot zu drehen, wenn es angeklickt wird. Es erstellt auch ein Speicherleck. Warum? Weil der Verweis auf el ist versehentlich in der Schließung für die anonyme innere Funktion gefangen gefangen. Dies erzeugt einen zirkulären Verweis zwischen einem JavaScript-Objekt (die Funktion) und einem nativen Objekt (el).

Bitte erklären Sie die oben genannten Ursachen der Leckage in einer einfachen und prägnanten Art und Weise, ich bekomme nicht den genauen Punkt.

Steht die Site/Seite wegen der Leckage vor einem Sicherheitsproblem? Wie vermeide ich sie? Welcher andere Code kann Speicherlecks verursachen? Wie kann ich feststellen, wenn ein Speicherleck aufgetreten ist?

Ich bin ein absoluter Anfänger zum Thema Speicherlecks. Könnte jemand diese Sachen für mich erklären, Schritt für Schritt? Auch kann jemand mir helfen, diese Aussage zu klären "Dies schafft eine zirkuläre Referenz zwischen einem JavaScript-Objekt (die Funktion) und einem nativen Objekt (el)."

+1

http://www.javascriptkit.com/javatutors/closuresleak/, http://www.google.com/search?q=explanation+of+javascript+memory+leaks – CBroe

+1

@GrantKiely seine von MDN –

+0

http: //javascript.crockford.com/memory/leak.html – undefined

Antwort

16

Es gibt zwei Konzepte, die Ihnen helfen werden, dieses Beispiel zu verstehen.

1) Verschlüsse

Die Definition eines Verschlusses ist, dass Every inner function enjoys access to its parent's function variables and parameters.

Wenn die addHandler() Funktion beendet hat, immer noch der anonyme Funktion der Zugriff auf die Variable el der Eltern.

2) Funktionen = Speicher

Jedes Mal, definieren Sie eine function ein neues Objekt erstellt wird. Was dieses Beispiel leicht verwirrend macht, ist, dass onclick ein Ereignis ist, das nur einmal auf ein DOM-Element gesetzt werden kann.

Also sicher el.onclick = function(){}; wird nur die alte Funktion richtig überschreiben?

Falsch! Jedes Mal, wenn addHandler ausgeführt wird, wird ein neues Funktionsobjekt erstellt.

Fazit:

Jedes Mal, wenn die Funktion läuft es wird ein neues Objekt mit einem Verschluss el enthält, erstellen. Da die anonyme Funktion den Zugriff auf el behält, kann der Garbage Collector den Speicher nicht aus dem Speicher entfernen.

Die anon-Funktion behält den Zugriff auf el, und el hat Zugriff auf die Funktion, das ist eine zirkuläre Referenz, die einen Speicherverlust in IE verursacht.

+8

* "In diesem Fall bezieht sich" dies "auf el." * ... das stimmt, aber diese besondere Tatsache hat nichts mit Schließungen zu tun. Dies ist eine Eigenschaft von Ereignishandlern, unabhängig davon, ob es sich um Sperrungen handelt oder nicht. Die Tatsache, dass "el" in der inneren Funktion zugänglich ist, ist wichtig. –

+0

@Grant Kiely Felix hat Recht. –

+0

@FelixKling Danke! Ich habe diesen Teil aus meiner Antwort entfernt. – gkiely

7

Immer wenn Sie eine Funktion in JavaScript definieren, wird dafür eine execution context erstellt; In diesem Zusammenhang Ausführung enthält Verweise auf alle Variablen im Rahmen Kette vom globalen Bereich gestartet werden den ganzen Weg bis zu den lokalen Bereich:

function test() 
{ 
    var el = document.getElementById('el'); 
    el.onclick = function() { 
     // execution context of this function: el, test 
     alert('hello world'); 
    } 
} 

Wenn test() getan wird, wird die anonyme Funktion noch nicht recycelt, weil es jetzt einem Element des DOM zugewiesen; d. h. es wird mit einer Eigenschaft des DOM-Elements referenziert.

Gleichzeitig ist das DOM-Element selbst Teil des Ausführungskontexts der Funktion und kann aufgrund der zyklischen Referenz nicht wiederverwendet werden, obwohl es nicht sofort offensichtlich ist, dass es tatsächlich verwendet wird. Sie können eine Demonstration davon in this answer finden.

Das heißt, heutzutage verwenden die meisten JavaScript-Engines (auch die im IE) eine fortgeschrittenere garbage collector, die ungenutzte Variablen viel besser identifizieren kann, unter Verwendung von Techniken wie mark-and-sweep oder generationelle/ephemere Garbage Collection.

Um sicherzustellen, dass Sie nicht in Probleme auf jeden Browser laufen (wenn auch aufgrund der typischen Lebensdauer einer Seite, dann ist dies meist theoretisch):

document.getElementById('el').onclick = function() { 
    alert('hello world'); 
} 
+0

konnte dies nicht bekommen "aber el wird auch nicht wiederverwendet, weil es Teil des Ausführungskontexts dieser Funktion ist." –

+1

@Maizere Der Ausführungskontext enthält (oder besser gesagt, Referenzen) 'el' und kann daher aufgrund der Referenzzählung nicht wiederverwendet werden. –

+0

so verursacht es ein Speicherleck ??? –

1

Auch den more information siehe Abschnitt von der MS-Artikel zu dem Problem:

Dieser Speicherverlust tritt auf, da DOM-Objekte Nicht-JScript-Objekte sind. DOM-Objekte befinden sich nicht im Mark-and-Sweep-Speicherbereinigungsschema von JScript. Daher wird der Zirkelverweis zwischen den DOM-Objekten und den JScript-Handlern nicht unterbrochen, bis der Browser vollständig die Seite abreißt.

aber, dass im Gegensatz zu beachten, was in diesem Artikel angegeben wird (Speicher zurückgewonnen werden, wenn der Browser auf eine neue Seite geht), bestätigt this article, dass ein Fehler im IE 6 der Speicher verursachte immer geleckt werden.

1

Die JavaScript-Speicherverwaltung funktioniert normalerweise so: "Solange es möglich ist, es zu erreichen, behalten Sie es". Dies ist im Grunde das Paradigma, das hinter jedem Speichermodell mit Speicherbereinigung steht.

Müllsammler neigen dazu, sehr gut zu sein, was sie tun, sie erkennen sogar, ob eine bestimmte Gruppe von Elementen nur innerhalb dieser Gruppe von Elementen erreichbar ist. Diese Gruppen werden auch als Zirkelverweis bezeichnet, denn wenn Sie den Verweisen folgen, werden Sie bei einem Element landen, das Sie bereits besucht haben: Sie haben einen Kreis ausgeführt.

jedoch in Ihrem Beispiel Sie haben tatsächlich zwei Objekte aus zwei verschiedenen „Welten“:

Circular references

Internet Explorer verwendet eine eigene Müllsammelsystem für diese, getrennt von dem Mechanismus, durch JavaScript verwendet . Es ist die Interaktion zwischen den beiden, die Speicherlecks verursachen kann.

Und genau das passiert und kann Speicherlecks verursachen.