Garbage Collection ist ein versteckter Mechanismus von JavaScript
. Normalerweise müssen wir uns nicht um die Garbage Collection kümmern, sondern müssen uns nur auf die Entwicklung von Funktionen konzentrieren. Dies bedeutet jedoch nicht, dass wir uns beim Schreiben JavaScript
zurücklehnen können. Da die von uns implementierten Funktionen immer komplexer werden und sich die Menge an Code ansammelt, treten Leistungsprobleme immer stärker in den Vordergrund. Wie man Code schreibt, der schneller ausgeführt wird und weniger Speicher beansprucht, ist die nie endende Aufgabe von Programmierern. Ein exzellenter Programmierer kann mit äußerst begrenzten Ressourcen immer erstaunliche Ergebnisse erzielen. Dies ist auch der Unterschied zwischen gewöhnlichen Wesen und distanzierten Göttern.
? Bei der Ausführung im Speicher des Computers belegen alle im Code definierten Variablen, Objekte und Funktionen einen bestimmten Speicherplatz im Speicher. Bei Computern ist der Speicherplatz eine sehr knappe Ressource. Wir müssen immer auf die Speichernutzung achten. Schließlich sind Speichermodule sehr teuer! Eine Variable, Funktion oder ein Objekt kann als Müll bezeichnet werden, wenn es nach der Erstellung für die nachfolgende Codeausführung nicht mehr benötigt wird.
Obwohl es für ein Computerprogramm sehr einfach ist, die Definition von Müll intuitiv zu verstehen, ist es für uns schwierig, zu einem bestimmten Zeitpunkt zu schließen, dass die aktuell vorhandenen Variablen, Funktionen oder Objekte in Zukunft nicht mehr verwendet werden. Um die Kosten für den Computerspeicher zu senken und die normale Ausführung von Computerprogrammen sicherzustellen, legen wir normalerweise fest, dass Objekte oder Variablen, die eine der folgenden Bedingungen erfüllen, Müll sind:
Variablen oder Objekte, auf die nicht verwiesen wird, entsprechen einem Haus ohne Tür, daher ist es unmöglich, sie zu verwenden. Obwohl unzugängliche Objekte miteinander verbunden sind, sind sie dennoch von außen unzugänglich und können daher nicht erneut verwendet werden. Objekte oder Variablen, die die oben genannten Bedingungen erfüllen, werden bei der zukünftigen Ausführung des Programms nie wieder verwendet, sodass sie sicher als Garbage Collection behandelt werden können.
Wenn wir durch die obige Definition die Objekte klären, die verworfen werden müssen, bedeutet das, dass die verbleibenden Variablen und Objekte keinen Müll enthalten?
NEIN! Der von uns derzeit identifizierte Müll ist nur ein Teil des gesamten Mülls. Es wird noch anderen Müll geben, der die oben genannten Bedingungen nicht erfüllt, aber nicht erneut verwendet wird.
Kann man sagen, dass Müll, der der obigen Definition entspricht, „absoluter Müll“ ist und anderer im Programm versteckter Müll „relativer Müll“ ist?
Der Garbage Collection-Mechanismus ( GC,Garbage Collection
) ist für die Wiederverwertung nutzloser Variablen und des während der Programmausführung belegten Speicherplatzes verantwortlich. Das Phänomen, dass ein Objekt immer noch im Speicher vorhanden ist, obwohl es keine Möglichkeit mehr hat, wieder verwendet zu werden, wird als Speicherverlust bezeichnet. Speicherlecks sind ein sehr gefährliches Phänomen, insbesondere bei Programmen mit langer Laufzeit. Wenn ein Programm einen Speicherverlust aufweist, belegt es immer mehr Speicherplatz, bis ihm der Speicher ausgeht.
Zeichenfolgen, Objekte und Arrays haben keine feste Größe, daher kann eine dynamische Speicherzuweisung für sie nur erfolgen, wenn ihre Größe bekannt ist. Jedes Mal, wenn ein JavaScript-Programm eine Zeichenfolge, ein Array oder ein Objekt erstellt, weist der Interpreter Speicher zum Speichern der Entität zu. Immer wenn auf diese Weise Speicher dynamisch zugewiesen wird, muss er schließlich freigegeben werden, damit er erneut verwendet werden kann. Andernfalls verbraucht der JavaScript-Interpreter den gesamten verfügbaren Speicher im System, was zum Absturz des Systems führt.
Der Garbage-Collection-Mechanismus von JavaScript
sucht zeitweise nach nutzlosen Variablen und Objekten (Müll) und gibt den von ihnen belegten Speicherplatz frei.
Verschiedene Programmiersprachen verwenden unterschiedliche Garbage-Collection-Strategien. C++
verfügt beispielsweise nicht über einen Garbage-Collection-Mechanismus. Die gesamte Speicherverwaltung hängt von den eigenen Fähigkeiten des Programmierers ab, was die Beherrschung von C++
erschwert. JavaScript
verwendet Erreichbarkeit , um den Speicher zu verwalten. Wörtlich bedeutet Erreichbarkeit, dass das Programm auf Variablen zugreifen und diese verwenden kann. Der von diesen Variablen belegte Speicher kann nicht freigegeben werden.
JavaScript
gibt einen inhärenten Satz erreichbarer Werte an, und die Werte im Satz sind inhärent erreichbar:
die oben genannten Variablen werden als Wurzeln bezeichnet und sind die obersten Knoten des Erreichbarkeitsbaums.
Eine Variable oder ein Objekt gilt als erreichbar, wenn sie direkt oder indirekt von der Stammvariablen verwendet wird.
Mit anderen Worten: Ein Wert ist erreichbar, wenn er über die Wurzel erreicht werden kann (z. B. Abcde
).
let people = { Jungs:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, Mädchen:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};
Der obige Code erstellt ein Objekt und weist es der Variablen people
zu. Die Variable people
enthält zwei Objekte, boys
und girls
, und boys
und girls
enthalten jeweils zwei Unterobjekte. Dadurch wird auch eine Datenstruktur erstellt, die 3
Ebenen von Referenzbeziehungen enthält (unabhängig von den Basistypdaten), wie unten gezeigt:
Unter ihnen ist der people
natürlich erreichbar, da es sich um eine globale Variable handelt. Die boys
und girls
sind indirekt erreichbar, da sie direkt von globalen Variablen referenziert werden. boys1
, boys2
, girls1
und girls2
sind ebenfalls erreichbare Variablen, da sie indirekt von globalen Variablen verwendet werden und über people.boys.boys
auf sie zugegriffen werden kann.
Wenn wir nach dem obigen Code den folgenden Code hinzufügen:
people.girls.girls2 = null; people.girls.girls1 = people.boys.boys2;
Dann sieht das obige Referenzhierarchiediagramm wie folgt aus:
Unter ihnen wurden girls1
und girls2
aufgrund der Trennung vom grils
-Knoten zu nicht erreichbaren Knoten, was bedeutet, dass sie vom Garbage-Collection-Mechanismus recycelt werden.
Und wenn wir zu diesem Zeitpunkt den folgenden Code ausführen:
people.boys.boys2 = null,
dann sieht das Referenzhierarchiediagramm wie folgt aus:
Zu diesem Zeitpunkt sind die Knoten boys
und boys2
zwar getrennt, aber aufgrund der Referenzbeziehung zwischen boys2
und girls
ist boys2
immer noch erreichbar und wird nicht vom Garbage-Collection-Mechanismus recycelt.
Das obige Assoziationsdiagramm beweist, warum der äquivalente Wert einer globalen Variablen als Wurzel bezeichnet wird, da dieser Werttyp im Assoziationsdiagramm normalerweise als Wurzelknoten des Beziehungsbaums erscheint.
let people = { Jungs:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, Mädchen:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};people.boys.boys2.girlfriend = people.girls.girls1; //boys2 bezieht sich auf girls1people.girls.girls1.boyfriend = people.boys.boys2; //girls1 bezieht sich auf guys2
Der obige Code erstellt eine Wechselbeziehung zwischen boys2
und girls1
.
Wenn wir an dieser Stelle die Assoziation zwischen boys
und boys2
abschneiden:
delete people.boys.boys2,
sieht das Assoziationsdiagramm zwischen Objekten wie folgt aus:
Offensichtlich gibt es keine nicht erreichbaren Knoten.
Wenn wir an diesem Punkt die Beziehungsverbindung boyfriend
unterbrechen:
delete people.girls.girls1,
wird das Beziehungsdiagramm zu:
Obwohl zu diesem Zeitpunkt immer noch eine girlfriend
zwischen boys2
und girls1
besteht, wird boys2
zu einem nicht erreichbaren Knoten und wird vom Garbage-Collection-Mechanismus zurückgefordert.
let people = { Jungs:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, Mädchen:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};delete people.boys;delete people.girls;
Das durch den obigen Code gebildete Referenzhierarchiediagramm lautet wie folgt:
Zu diesem Zeitpunkt besteht zwar immer noch eine gegenseitige Referenzbeziehung zwischen den Objekten innerhalb des gepunkteten Felds, diese Objekte sind jedoch ebenfalls nicht erreichbar und werden vom Garbage-Collection-Mechanismus gelöscht. Diese Knoten haben ihre Beziehung zur Wurzel verloren und sind nicht mehr erreichbar.
Die sogenannte Referenzzählung zählt, wie der Name schon sagt, jedes Mal, wenn auf ein Objekt verwiesen wird. Durch das Hinzufügen einer Referenz wird es um eins erhöht, und durch das Löschen einer Referenz wird es um eins verringert Wenn die Zahl 0 wird, gilt sie als Garbage und löscht somit Objekte, um Speicher freizugeben.
Zum Beispiel:
let user = {username:'xiaoming'}; //Das Objekt wird von der Benutzervariablen referenziert, count +1 let user2 = user; //Das Objekt wird von einer neuen Variablen referenziert und die Anzahl beträgt +1 Benutzer = null; //Die Variable verweist nicht mehr auf das Objekt, der Zähler ist -1 Benutzer2 = null; //Die Variable verweist nicht mehr auf das Objekt, ungerade Zahl -1 // Zu diesem Zeitpunkt beträgt die Anzahl der Objektreferenzen 0 und wird gelöscht.
Obwohl die Referenzzählmethode sehr vernünftig erscheint, gibt es tatsächlich offensichtliche Lücken im Speicherrecyclingmechanismus, der die Referenzzählmethode verwendet.
Zum Beispiel:
let boy = {}; let girl = {}; boy.girlfriend = Mädchen; girl.boyfriend = Junge; Junge = null; girl = null;
Der obige Code enthält gegenseitige Referenzen zwischen boy
und girl
. Durch das Zählen werden die Referenzen in boy
und girl
gelöscht und die beiden Objekte werden nicht recycelt. Aufgrund der Existenz von Zirkelverweisen wird der Referenzzähler der beiden anonymen Objekte niemals auf Null zurückkehren, was zu einem Speicherverlust führt.
In C++
gibt es ein Konzept für intelligente Zeiger ( shared_ptr
). Programmierer können den intelligenten Zeiger verwenden, um den Objektdestruktor zum Freigeben des Referenzzählers zu verwenden. Bei Zirkelverweisen kommt es jedoch zu Speicherlecks.
Glücklicherweise hat JavaScript
eine andere sicherere Strategie übernommen, die das Risiko von Speicherverlusten weitgehend vermeidet.
Markieren mark and sweep
ist ein von der JavaScript
Engine übernommener Garbage-Collection-Algorithmus. Sein Grundprinzip besteht darin, von der Wurzel aus zu beginnen, die Referenzbeziehung zwischen Variablen in der Breite zu durchqueren und die durchquerten Variablen zu markieren (优秀员工徽章
). . Nicht markierte Objekte werden endgültig gelöscht.
Der grundlegende Prozess des Algorithmus ist wie folgt:
2
Bis kein neuer Excellent-Mitarbeiter beitritt;Beispiel:
Wenn es in unserem Programm eine Objektreferenzbeziehung gibt, wie unten gezeigt:
Wir können deutlich erkennen, dass es auf der rechten Seite des gesamten Bildes eine „erreichbare Insel“ gibt. Von der Wurzel aus kann die Insel niemals erreicht werden. Aber der Garbage Collector hat keine Gottesperspektive wie wir. Er markiert den Wurzelknoten nur auf der Grundlage des Algorithmus als herausragenden Mitarbeiter.
Beginnen Sie dann mit den herausragenden Mitarbeitern und suchen Sie alle von den herausragenden Mitarbeitern genannten Knoten, z. B. die drei Knoten im gepunkteten Kästchen in der Abbildung oben. Anschließend markieren Sie die neu gefundenen Knoten als herausragende Mitarbeiter.
Der Vorgang des Suchens und Markierens wird wiederholt, bis alle gefundenen Knoten erfolgreich markiert wurden.
Schließlich wird der in der folgenden Abbildung dargestellte Effekt erzielt:
Da die Inseln auf der rechten Seite nach Ende des Algorithmus-Ausführungszyklus immer noch nicht markiert sind, können diese Knoten von der Garbage-Collector-Aufgabe nicht erreicht werden und werden schließlich gelöscht.
Kinder, die Datenstrukturen und Algorithmen studiert haben, werden möglicherweise überrascht sein, dass es sich hierbei um eine Graphendurchquerung handelt, ähnlich wie bei verbundenen Graphalgorithmen.
Die Garbage Collection ist eine umfangreiche Aufgabe, insbesondere wenn die Codemenge sehr groß ist und die häufige Ausführung des Garbage Collection-Algorithmus die Ausführung des Programms erheblich beeinträchtigt. Der JavaScript
Algorithmus hat zahlreiche Optimierungen bei der Garbage Collection vorgenommen, um sicherzustellen, dass das Programm effizient ausgeführt werden kann und gleichzeitig die normale Ausführung der Recyclingarbeiten gewährleistet ist.
Strategien zur Leistungsoptimierung umfassen in der Regel die folgenden Punkte:
JavaScript
Programme behalten während der Ausführung eine beträchtliche Anzahl von Variablen bei, und häufiges Scannen dieser Variablen verursacht erheblichen Overhead. Allerdings haben diese Variablen im Lebenszyklus ihre eigenen Eigenschaften. Beispielsweise werden lokale Variablen häufig erstellt, schnell verwendet und dann verworfen, während globale Variablen lange Zeit den Speicher belegen. JavaScript
verwaltet die beiden Objekttypen getrennt. Bei lokalen Variablen, die schnell erstellt, verwendet und verworfen werden, führt der Garbage Collector häufig einen Scan durch, um sicherzustellen, dass diese Variablen schnell bereinigt werden, nachdem sie nicht mehr verwendet werden. Reduzieren Sie bei Variablen, die den Speicher über einen längeren Zeitraum belegen, die Häufigkeit ihrer Überprüfung, wodurch ein gewisser Overhead eingespart wird.
Die inkrementelle Idee ist in der Leistungsoptimierung weit verbreitet und kann auch für die Speicherbereinigung verwendet werden. Wenn die Anzahl der Variablen sehr groß ist, ist es offensichtlich sehr zeitaufwändig, alle Variablen gleichzeitig zu durchlaufen und ausstehende Mitarbeiternoten zu vergeben, was zu Verzögerungen bei der Programmausführung führt. Daher teilt die Engine die Garbage-Collection-Arbeit in mehrere Unteraufgaben auf und führt jede kleine Aufgabe während der Ausführung des Programms nach und nach aus. Dies führt zu einer gewissen Wiederherstellungsverzögerung, führt jedoch normalerweise nicht zu offensichtlichen Programmverzögerungen.
CPU
CPU
bei komplexen Programmen nicht immer. Dies liegt hauptsächlich daran, dass CPU
sehr schnell arbeitet und die Peripherie IO
oft um mehrere Größenordnungen langsamer ist Leerlauf Dies ist eine sehr effektive Methode zur Leistungsoptimierung und hat im Grunde keine negativen Auswirkungen auf das Programm selbst. Diese Strategie ähnelt dem Leerlaufzeit-Upgrade des Systems, und Benutzer sind sich der Hintergrundausführung überhaupt nicht bewusst.
Die Hauptaufgabe dieses Artikels besteht lediglich darin, Garbage-Collection-Mechanismen, häufig verwendete Strategien und Optimierungsmethoden zu beenden. Es ist nicht beabsichtigt, jedem ein detailliertes Verständnis der Hintergrundausführungsprinzipien der Engine zu vermitteln.
Durch diesen Artikel sollten Sie verstehen:
JavaScript
. Sie wird im Hintergrund ausgeführt und erfordert nicht, dass wir uns darum kümmern.