Was ist JavaScript-Speicherbereinigung?

Antworten:

192

Eric Lippert hat vor einiger Zeit einen ausführlichen Blog-Beitrag zu diesem Thema geschrieben (zusätzlich zu VBScript ). Genauer gesagt schrieb er über JScript , die Microsoft-eigene Implementierung von ECMAScript, obwohl sie JavaScript sehr ähnlich ist. Ich würde mir vorstellen, dass Sie davon ausgehen können, dass die überwiegende Mehrheit des Verhaltens für die JavaScript-Engine von Internet Explorer gleich ist. Natürlich wird die Implementierung von Browser zu Browser unterschiedlich sein, obwohl ich vermute, dass Sie eine Reihe der gemeinsamen Prinzipien auf andere Browser anwenden können.

Zitiert von dieser Seite:

JScript verwendet einen nicht generationenübergreifenden Mark-and-Sweep-Garbage-Collector. Es funktioniert so:

  • Jede Variable, die "im Gültigkeitsbereich" ist, wird als "Scavenger" bezeichnet. Ein Aasfresser kann sich auf eine Zahl, ein Objekt, eine Zeichenfolge usw. beziehen. Wir führen eine Liste von Scavengern - Variablen werden in die Scav-Liste verschoben, wenn sie in den Gültigkeitsbereich gelangen, und aus der Scav-Liste entfernt, wenn sie den Gültigkeitsbereich verlassen.

  • Hin und wieder läuft der Müllsammler. Zuerst wird jedes Objekt, jede Variable, jede Zeichenfolge usw. markiert - der gesamte vom GC verfolgte Speicher. (JScript verwendet die VARIANT-Datenstruktur intern und es gibt viele zusätzliche nicht verwendete Bits in dieser Struktur, daher setzen wir nur eines davon.)

  • Zweitens werden die Markierungen auf den Aasfressern und das vorübergehende Schließen von Aasfresserreferenzen gelöscht. Wenn also ein Scavenger-Objekt auf ein Nicht-Scavenger-Objekt verweist, löschen wir die Bits auf dem Nicht-Scavenger und auf allem, worauf es sich bezieht. (Ich verwende das Wort "Schließung" in einem anderen Sinne als in meinem früheren Beitrag.)

  • Zu diesem Zeitpunkt wissen wir, dass dem gesamten noch markierten Speicher Speicher zugewiesen ist, der von keinem Pfad aus einer In-Scope-Variablen erreicht werden kann. Alle diese Objekte werden angewiesen, sich selbst abzureißen, wodurch alle Zirkelverweise zerstört werden.

Der Hauptzweck der Speicherbereinigung besteht darin, dem Programmierer zu ermöglichen, sich keine Gedanken über die Speicherverwaltung der von ihm erstellten und verwendeten Objekte zu machen, obwohl dies natürlich manchmal nicht zu vermeiden ist. Es ist immer von Vorteil, zumindest eine ungefähre Vorstellung davon zu haben, wie die Speicherbereinigung funktioniert .

Historischer Hinweis: Eine frühere Überarbeitung der Antwort hatte einen falschen Verweis auf den deleteBediener. In JavaScript entfernt der deleteOperator eine Eigenschaft aus einem Objekt und unterscheidet sich grundlegend von deleteC / C ++.

Noldorin
quelle
27
Der Apple-Leitfaden ist fehlerhaft: Der Autor verwendet ihn deletefalsch. Beispiel: Im ersten Beispiel sollten Sie stattdessen delete foozuerst den Ereignis-Listener über entfernen window.removeEventListener()und dann foo = nulldie Variable überschreiben. im IE hätte delete window.foo(aber nicht delete foo) auch funktioniert, wenn fooes global gewesen wäre, aber selbst dann würde es nicht in FF oder Opera funktionieren
Christoph
3
Beachten Sie, dass der Artikel von Eric "nur für historische Zwecke" betrachtet werden sollte. Aber es ist immer noch informativ.
Peter Ivan
2
Beachten Sie auch, dass IE 6 und 7 KEINEN nicht generationenübergreifenden Mark-and-Sweep-Garbage-Collector verwenden. Sie verwenden einen einfachen Garbage Collector für die Referenzzählung, der anfälliger für Zirkelreferenzprobleme bei der Garbage Collection ist.
Doug
1
ECMAScript deleteist ein unärer Operator (ein Ausdruck), keine Anweisung (dh :) delete 0, delete 0, delete 3. Es sieht aus wie eine Anweisung, wenn sie durch eine Ausdrucksanweisung ausgedrückt wird.
Hydroper
Ja, die Antwort zu der Zeit ist jetzt veraltet. Ab 2012 verwenden moderne Browser einen Mark / Sweep-Algorithmus. Daher ist er nicht mehr vom Umfang abhängig. Referenzierung: developer.mozilla.org/en-US/docs/Web/JavaScript/…
sksallaj
52

Achten Sie auf Zirkelverweise, wenn es sich um DOM-Objekte handelt:

Speicherverlustmuster in JavaScript

Beachten Sie, dass der Speicher nur wiederhergestellt werden kann, wenn keine aktiven Verweise auf das Objekt vorhanden sind. Dies ist eine häufige Gefahr bei Schließungen und Ereignishandlern, da einige JS-Engines nicht prüfen, auf welche Variablen in inneren Funktionen tatsächlich verwiesen wird, sondern nur alle lokalen Variablen der umschließenden Funktionen beibehalten.

Hier ist ein einfaches Beispiel:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Eine naive JS-Implementierung kann nicht erfasst werden, bigStringsolange der Ereignishandler verfügbar ist. Es gibt verschiedene Möglichkeiten, um dieses Problem zu lösen, z. B. die Einstellung bigString = nullam Ende von init()( deletefunktioniert nicht für lokale Variablen und Funktionsargumente: deleteEntfernt Eigenschaften von Objekten, und auf das Variablenobjekt kann nicht zugegriffen werden - ES5 im strengen Modus löst sogar ein aus, ReferenceErrorwenn Sie es versuchen um eine lokale Variable zu löschen!).

Ich empfehle, unnötige Schließungen so weit wie möglich zu vermeiden, wenn Sie den Speicherverbrauch berücksichtigen.

Christoph
quelle
20
Der DOM-Zirkelverweisfehler ist spezifisch für JScript - kein anderer Browser als IE leidet darunter. Tatsächlich bin ich mir ziemlich sicher, dass die ECMAScript-Spezifikation ausdrücklich besagt, dass der GC in der Lage sein muss, solche Zyklen zu handhaben: - /
olliej
@olliej: Ich sehe keine Erwähnung des GC in der ECMAScript-Spezifikation .
Janus Troelsen
16

Gutes Zitat aus einem Blog

Die DOM-Komponente ist "Garbage Collected", ebenso wie die JScript-Komponente. Wenn Sie also ein Objekt in einer der Komponenten erstellen und dann den Überblick über dieses Objekt verlieren, wird es schließlich bereinigt.

Zum Beispiel:

function makeABigObject() {
var bigArray = new Array(20000);
}

Wenn Sie diese Funktion aufrufen, erstellt die JScript-Komponente ein Objekt (mit dem Namen bigArray), auf das innerhalb der Funktion zugegriffen werden kann. Sobald die Funktion jedoch zurückkehrt, verlieren Sie den Überblick über bigArray, da es keine Möglichkeit mehr gibt, darauf zu verweisen. Nun, die JScript-Komponente erkennt, dass Sie den Überblick verloren haben, und so wird bigArray bereinigt - sein Speicher wird zurückgefordert. Das Gleiche funktioniert in der DOM-Komponente. Wenn Sie sagen document.createElement('div')oder etwas Ähnliches, erstellt die DOM-Komponente ein Objekt für Sie. Sobald Sie den Überblick über dieses Objekt verlieren, bereinigt die DOM-Komponente das zugehörige Objekt.

TStamper
quelle
13

Nach meinem besten Wissen werden die Objekte von JavaScript regelmäßig mit Müll gesammelt, wenn keine Verweise mehr auf das Objekt vorhanden sind. Dies geschieht automatisch, aber wenn Sie mehr über die Funktionsweise auf C ++ - Ebene erfahren möchten, ist es sinnvoll, sich den WebKit- oder V8-Quellcode anzusehen

Normalerweise müssen Sie jedoch nicht darüber nachdenken. In älteren Browsern wie IE 5.5 und früheren Versionen von IE 6 und möglicherweise aktuellen Versionen führen Schließungen jedoch zu Zirkelverweisen, die, wenn sie nicht aktiviert sind, Speicherplatz verbrauchen. In dem speziellen Fall, den ich mit Schließungen meine, haben Sie einen JavaScript-Verweis auf ein Dom-Objekt und ein Objekt auf ein DOM-Objekt hinzugefügt, das auf das JavaScript-Objekt zurückgeht. Grundsätzlich könnte es niemals gesammelt werden und würde schließlich dazu führen, dass das Betriebssystem in Test-Apps instabil wird, die Schleifen bilden, um Abstürze zu verursachen. In der Praxis sind diese Lecks normalerweise klein, aber um Ihren Code sauber zu halten, sollten Sie den JavaScript-Verweis auf das DOM-Objekt löschen.

Normalerweise ist es eine gute Idee, das Schlüsselwort delete zu verwenden, um große Objekte wie JSON-Daten, die Sie zurückerhalten haben, sofort zu de-referenzieren und alles zu tun, was Sie damit tun müssen, insbesondere in der mobilen Webentwicklung. Dies bewirkt, dass der nächste Durchlauf des GC dieses Objekt entfernt und seinen Speicher freigibt.

Heat Miser
quelle
Ist das Problem mit JavaScript -> DOM -> JavaScript-Zirkelreferenz in neueren Versionen des IE gelöst? Wenn ja, seit wann? Ich dachte, es sei architektonisch sehr tief und es sei unwahrscheinlich, dass es jemals repariert wird. Hast du irgendwelche Quellen?
Erikkallen
Nur anekdotisch. Ich habe die verrückten Lecks in IE 8 im Standardmodus nicht bemerkt, nicht im defekten Modus. Ich werde meine Antwort anpassen.
Heat Miser
1
@erikkallen: Ja, der GC-Fehler wurde in IE-Versionen 8+ behoben, da die älteren einen sehr naiven Garbage Collection-Algorithmus verwendeten, der es unmöglich machte, ein Paar von Objekten zu GCen, die aufeinander verweisen. Die neueren mark-and-sweepAlgorithmen sorgen dafür .
Kumarshar
6

Garbage Collection (GC) ist eine Form der automatischen Speicherverwaltung, bei der nicht mehr benötigte Objekte entfernt werden.

Jeder Prozess, der sich mit dem Speicher befasst, führt die folgenden Schritte aus:

1 - Weisen Sie Ihren benötigten Speicherplatz zu

2 - etwas verarbeiten

3 - Geben Sie diesen Speicherplatz frei

Es gibt zwei Hauptalgorithmen, mit denen erkannt wird, welche Objekte nicht mehr benötigt werden.

Garbage Collection mit Referenzzählung : Dieser Algorithmus reduziert die Definition von "ein Objekt wird nicht mehr benötigt" auf "ein Objekt hat kein anderes Objekt, das darauf verweist". Das Objekt wird entfernt, wenn kein Referenzpunkt darauf vorhanden ist

Mark-and-Sweep-Algorithmus : Verbinden Sie jedes Objekt mit der Root-Quelle. Jedes Objekt stellt keine Verbindung zu root oder einem anderen Objekt her. Dieses Objekt wird entfernt.

Derzeit verwenden die meisten modernen Browser den zweiten Algorithmus.

Ahmed Gaber - Biga
quelle
1
Eine Quelle hierfür finden Sie im MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos
4

"In der Informatik ist Garbage Collection (GC) eine Form der automatischen Speicherverwaltung. Der Garbage Collector oder nur der Collector versucht, Müll oder Speicher zurückzugewinnen, der von Objekten verwendet wird, auf die die Anwendung nie wieder zugreifen oder die sie mutieren werden."

Alle JavaScript-Engines haben ihre eigenen Garbage Collectors und können sich unterscheiden. Meistens muss man sich nicht mit ihnen befassen, weil sie einfach das tun, was sie tun sollen.

Das Schreiben von besserem Code hängt hauptsächlich davon ab, wie gut Sie die Programmierprinzipien, die Sprache und die jeweilige Implementierung kennen.

mtasic85
quelle
1

Was ist JavaScript-Speicherbereinigung?

Überprüfen Sie dies

Was ist für einen Webprogrammierer wichtig, um die JavaScript-Speicherbereinigung zu verstehen, um besseren Code schreiben zu können?

In Javascript interessiert Sie die Speicherzuweisung und Freigabe nicht. Das ganze Problem wird vom Javascript-Interpreter verlangt. Lecks sind in Javascript immer noch möglich, aber sie sind Fehler des Interpreters. Wenn Sie sich für dieses Thema interessieren, können Sie mehr unter www.memorymanagement.org lesen

dfa
quelle
Welches der verschiedenen Speicherverwaltungssysteme in dem Artikel, auf den Sie verlinken, wird von JavaScript verwendet? "Lecks sind in Javascript immer noch möglich, aber sie sind Fehler des Dolmetschers." - Das bedeutet nicht, dass JS-Programmierer das gesamte Problem einfach ignorieren können. Beispielsweise gibt es in älteren IE-Versionen ein ziemlich bekanntes Problem mit JS <-> DOM-Zirkelreferenzen, das Sie in Ihrem JS-Code umgehen können. Auch die Funktionsweise von JS-Verschlüssen ist ein Designmerkmal, kein Fehler, aber Sie können größere Speicherblöcke als beabsichtigt binden, wenn Sie Verschlüsse "unangemessen" verwenden (ich sage nicht, dass Sie sie nicht verwenden).
nnnnnn
3
Speicherlecks sind ein Biest in JavaScript. Wenn Sie eine einfache "College-Projekt" -Anwendung schreiben, dann keine Sorge. Wenn Sie jedoch anfangen, leistungsstarke Apps auf Unternehmensebene zu schreiben, ist die Speicherverwaltung in JavaScript ein Muss.
Doug
1

Unter Windows können Sie Drip.exe verwenden , um Speicherlecks zu finden oder um zu überprüfen, ob Ihre kostenlose Mem-Routine funktioniert.

Es ist ganz einfach, geben Sie einfach eine Website-URL ein und Sie sehen den Speicherverbrauch des integrierten IE-Renderers. Klicken Sie dann auf Aktualisieren. Wenn sich der Speicher vergrößert, haben Sie irgendwo auf der Webseite einen Speicherverlust festgestellt. Dies ist jedoch auch sehr nützlich, um festzustellen, ob Routinen zum Freigeben von Speicher für den Internet Explorer funktionieren.

powtac
quelle
1

Referenztypen speichern das Objekt nicht direkt in der Variablen, der es zugewiesen ist, sodass die Objektvariable in diesem Beispiel die Objektinstanz nicht enthält. Stattdessen enthält es einen Zeiger (oder eine Referenz) auf die Stelle im Speicher, an der das Objekt vorhanden ist

var object = new Object();

Wenn Sie eine Variable einer anderen zuweisen, erhält jede Variable eine Kopie des Zeigers, und beide verweisen immer noch auf dasselbe Objekt im Speicher.

var object1 = new Object();
var object2 = object1;

Zwei Variablen, die auf ein Objekt zeigen

JavaScript ist eine durch Müll gesammelte Sprache, sodass Sie sich bei der Verwendung von Referenztypen keine Gedanken über die Speicherzuweisung machen müssen. Es ist jedoch am besten, nicht mehr benötigte Objekte zu dereferenzieren , damit der Garbage Collector diesen Speicher freigeben kann. Der beste Weg, dies zu tun, besteht darin, die Objektvariable auf null zu setzen.

var object1 = new Object();
// do something
object1 = null; // dereference

Das Dereferenzieren von Objekten ist besonders wichtig in sehr großen Anwendungen, die Millionen von Objekten verwenden.

aus den Prinzipien des objektorientierten JavaScript - NICHOLAS C. ZAKAS

Ani Naslyan
quelle