1. ÜbersichtMit der kontinuierlichen Entwicklung der Softwareentwicklungsbranche ist Leistungsoptimierung zu einem unvermeidlichen Thema geworden. Welche Art von Verhalten kann also als Leistungsoptimierung angesehen werden? Grundsätzlich kann jedes Verhalten, das die Betriebseffizienz verbessern und den Betriebsaufwand reduzieren kann, als Optimierungsvorgang betrachtet werden. Dies bedeutet, dass es in der offenen Softwarebranche viele Bereiche geben muss, in denen es einer Optimierung bedarf, insbesondere im Front-End-Entwicklungsprozess, wo Leistungsoptimierung als allgegenwärtig angesehen werden kann. So lassen sich etwa das für die Anforderung von Ressourcen genutzte Netzwerk, die Methode der Datenübertragung oder das im Entwicklungsprozess verwendete Framework optimieren. In diesem Kapitel wird die Optimierung der JavaScript-Sprache selbst untersucht, von der Nutzung kognitiven Speicherplatzes bis zur Speicherbereinigung, damit effizienter JavaScript-Code geschrieben werden kann. 2. SpeicherverwaltungMit der kontinuierlichen Weiterentwicklung der Hardwaretechnologie in den letzten Jahren verfügen höhere Programmiersprachen über integrierte GC-Mechanismen, sodass Entwickler die entsprechende Funktionsentwicklung normal abschließen können, ohne besonders auf die Verwendung des Speicherplatzes achten zu müssen. Warum müssen wir die Speicherverwaltung noch einmal erwähnen? Lassen Sie es uns anhand eines sehr einfachen Codes erklären. Definieren Sie zunächst eine normale Funktion fn, deklarieren Sie dann ein Array im Funktionskörper und weisen Sie dem Array dann Werte zu. Dabei ist zu beachten, dass bei der Wertezuweisung bewusst eine relativ große Zahl als Index gewählt wird. Der Zweck besteht darin, der aktuellen Funktion beim Aufruf möglichst viel Speicherplatz zuzuweisen. Funktion fn() { Anzahl der Einträge = []; arrlist[100000] = 'das ist ein lg'; } fn() Bei der Ausführung dieser Funktion gibt es kein syntaktisches Problem. Bei der Überwachung des Speichers mit dem entsprechenden Leistungsüberwachungstool wird jedoch festgestellt, dass die Speicheränderung weiterhin linear zunimmt und es dabei zu keinem Abfall kommt. Dies deutet auf einen Speicherverlust hin. Wenn Sie den Speicherverwaltungsmechanismus beim Schreiben von Code nicht gut genug verstehen, schreiben Sie Code mit Speicherproblemen, der nicht leicht zu erkennen ist. Wenn zu viel Code dieser Art vorhanden ist, kann das Programm unerwartete Fehler aufweisen. Daher ist es äußerst wichtig, die Speicherverwaltung zu beherrschen. Sehen wir uns also an, was Speicherverwaltung ist. Wie das Wort schon sagt, besteht der Speicher eigentlich aus lesbaren und beschreibbaren Einheiten, die einen Operationsraum darstellen. Was das Management hier bewusst betont, ist, dass die Leute die Initiative ergreifen sollten, um diesen Platz zu beantragen, zu nutzen und freizugeben. Selbst mit Hilfe einiger APIs können die Leute dies schließlich selbstständig tun. Speicherverwaltung bedeutet daher, dass Entwickler aktiv Speicherplatz beantragen, Speicherplatz nutzen und Speicherplatz freigeben können. Der Vorgang ist daher denkbar einfach und besteht aus insgesamt drei Schritten: Beantragung, Nutzung und Freigabe. Zurück zu JavaScript: Tatsächlich führt JavaScript diesen Prozess wie andere Sprachen auch in drei Schritten aus, ECMAScript bietet jedoch keine entsprechende Operations-API. Daher kann JavaScript nicht wie C oder C++ sein, wo Entwickler die entsprechende API aktiv aufrufen können, um den Speicherplatz zu verwalten. Dies hat jedoch keinen Einfluss auf unsere Demonstration, wie der Lebenszyklus eines Raums durch JavaScript-Skripte abgeschlossen wird. Der Vorgang ist ganz einfach. Zuerst müssen Sie einen Platz beantragen. Zweitens müssen Sie den Platz nutzen. Drittens müssen Sie den Platz freigeben. In JavaScript wird keine entsprechende API direkt bereitgestellt, sodass die JavaScript-Ausführungs-Engine nur dann automatisch einen entsprechenden Speicherplatz zuordnen kann, wenn sie auf eine Variablendefinitionsanweisung stößt. Hier definieren wir zuerst eine Variable obj und richten sie dann auf ein leeres Objekt. Seine Verwendung ist eigentlich ein Lese-/Schreibvorgang. Schreiben Sie einfach bestimmte Daten in dieses Objekt, beispielsweise ein yd. Schließlich können Sie es freigeben. Auch hier gibt es in JavaScript keine entsprechende Freigabe-API, sodass hier eine indirekte Methode verwendet werden kann, z. B. das direkte Setzen auf null. let obj = {} Objektname = "yd" obj = null Dies entspricht derzeit der Implementierung der Speicherverwaltung in JavaScript gemäß einem Speicherverwaltungsprozess. Später können Sie sich den Speichertrend einfach in solchen Leistungsüberwachungstools ansehen. 3. MüllabfuhrZunächst einmal: Welche Art von Inhalt wird in JavaScript als Müll betrachtet? Das Konzept von Müll wird auch in nachfolgenden GC-Algorithmen vorhanden sein, und die beiden sind tatsächlich genau dasselbe. Lassen Sie es uns hier einheitlich erklären. Die Speicherverwaltung in JavaScript erfolgt automatisch. Bei jeder Erstellung eines Objekts, Arrays oder einer Funktion wird automatisch der entsprechende Speicherplatz zugewiesen. Wenn der nachfolgende Programmcode ausgeführt wird und einige Objekte über einige Referenzbeziehungen nicht mehr gefunden werden können, werden diese Objekte als Müll betrachtet. Oder diese Objekte sind tatsächlich bereits vorhanden, aber aufgrund unangemessener Syntax- oder Strukturfehler im Code besteht keine Möglichkeit, diese Objekte zu finden. In diesem Fall werden diese Objekte ebenfalls als Müll bezeichnet. Nach der Entdeckung von Müll wird die JavaScript-Ausführungs-Engine aktiv und recycelt den vom Müll belegten Objektspeicherplatz. Dieser Vorgang wird als Garbage Collection bezeichnet. Hier werden mehrere kleine Konzepte verwendet. Das erste ist die Referenz und das zweite der Zugriff von der Wurzel aus. Dieser Vorgang wird auch in nachfolgenden GCs häufig erwähnt. Hier möchte ich einen weiteren Begriff erwähnen, nämlich erreichbares Objekt. Zunächst einmal ist es sehr einfach, erreichbares Objekt in JavaScript zu verstehen, d. h. das Objekt, auf das zugegriffen werden kann. Der Zugriff kann über eine bestimmte Referenz oder über die Bereichskette im aktuellen Kontext erfolgen. Solange es gefunden werden kann, gilt es als erreichbar. Allerdings gibt es hier eine kleine Standardeinschränkung, nämlich dass es nur dann als erreichbar gilt, wenn es von der Wurzel aus gefunden werden kann. Besprechen wir also, was die Wurzel ist. In JavaScript kann das aktuelle globale Variablenobjekt als Wurzel betrachtet werden, also der sogenannte globale Ausführungskontext. Um es einfach zusammenzufassen: Bei der Garbage Collection in JavaScript geht es eigentlich darum, den Müll zu finden und dann die JavaScript-Ausführungs-Engine den Speicherplatz freigeben und wiederverwenden zu lassen. Hier werden Referenzen und erreichbare Objekte verwendet. Als nächstes werden wir versuchen, anhand von Code zu sehen, wie Referenzen und erreichbare Objekte in JavaScript dargestellt werden. Definieren Sie zunächst eine Variable. Um den Wert später zu ändern, verwenden Sie das Schlüsselwort let, um ein obj zu definieren, das auf ein Objekt verweist. Geben Sie ihm der Einfachheit halber einen Namen namens let obj = {name: 'xiaoming'} Nach dem Schreiben dieser Codezeile entspricht dies tatsächlich der Referenzierung dieses Bereichs durch das aktuelle Obj-Objekt, und hier wird eine Referenz angezeigt. Im globalen Ausführungskontext kann obj von der Wurzel aus gefunden werden, was bedeutet, dass obj erreichbar ist, was indirekt bedeutet, dass der Objektraum des aktuellen Xiaoming erreichbar ist. Definieren Sie dann eine Variable neu. Lassen Sie beispielsweise Ali gleich Obj sein. Man kann davon ausgehen, dass Xiao Mings Raum eine weitere Referenz hat. Hier kommt es zu einer Änderung des Referenzwerts und dieses Konzept wird im nachfolgenden Referenzzählalgorithmus verwendet. let obj = {name: 'xiaoming'} lass ali = obj Lassen Sie uns noch etwas tun: Suchen Sie obj direkt und weisen Sie es erneut auf null zu. Nachdem Sie dies getan haben, können Sie darüber nachdenken. Der Objektraum von Xiao Ming selbst hat zwei Referenzen. Durch die Ausführung des Nullzuweisungscodes wird die Referenz von obj auf den Xiaoming-Bereich abgeschnitten. Ist Xiao Mings Objekt jetzt noch erreichbar? Natürlich ist es das. Da „ali“ immer noch auf einen solchen Objektraum verweist, ist es immer noch ein erreichbares Objekt. Dies ist die Haupterklärung eines Zitats, und wir sehen übrigens auch eine erreichbare. Als nächstes nehmen wir ein weiteres Beispiel, um die erreichbaren Operationen im aktuellen JavaScript zu veranschaulichen. Dies muss jedoch im Voraus erklärt werden. Um den Mark-Sweep-Algorithmus im nachfolgenden GC zu erleichtern, wird dieses Beispiel etwas komplizierter sein. Definieren Sie zunächst eine Funktion mit dem Namen objGroup, legen Sie die beiden Parameter obj1 und obj2 fest, lassen Sie obj1 über ein Attribut auf obj2 zeigen und lassen Sie dann obj2 ebenfalls über ein Attribut auf obj1 zeigen. Verwenden Sie dann das Schlüsselwort „return“, um ein Objekt direkt zurückzugeben, geben Sie obj1 bis o1 zurück und legen Sie dann o2 fest, damit obj2 gefunden wird. Rufen Sie diese Funktion nach Abschluss extern auf, legen Sie eine Variable für den Empfang fest und obj ist gleich dem Ergebnis des objGroup-Aufrufs. Die beiden übergebenen Parameter sind zwei Objekte obj1 und obj2. Funktion Objektgruppe(Objekt1, Objekt2) { obj1.nächstes = obj2; obj2.prev = obj1; } let obj = objGroup({name: 'obj1'}, {name: 'obj2'}); konsole.log(obj); Führen Sie es aus und Sie werden feststellen, dass Sie ein Objekt erhalten haben. Das Objekt enthält obj1 und obj2, und obj1 und obj2 verweisen über ein Attribut aufeinander. { o1: {name: 'obj1', next: {name: 'obj2', prev: [Rundschreiben]}}, o2: {name: 'obj2', next: {name: 'obj1', next: [Rundschreiben]}} } Bei der Analyse des Codes können wir zunächst ein erreichbares Objekt obj vom globalen Stamm aus finden. Es zeigt über einen Funktionsaufruf auf einen Speicherplatz, der o1 und o2 enthält, wie oben gezeigt. Dann zeigen die entsprechenden Attribute innerhalb von o1 und o2 einfach auf einen Obj1-Bereich und einen Obj2-Bereich. obj1 und obj2 verweisen über next und prev aufeinander, sodass alle im Code vorkommenden Objekte von der Wurzel aus durchsucht werden können. Egal, wie schwierig es ist, es zu finden, Sie können es trotzdem finden. Fahren Sie also mit der weiteren Analyse fort. Wenn die Delete-Anweisung verwendet wird, wird die Referenz von o1 in obj und die Referenz von obj2 auf obj1 direkt gelöscht. An diesem Punkt bedeutet dies, dass es keine Möglichkeit gibt, den Objektbereich obj1 direkt zu finden. Daher wird dies hier als Garbage-Operation betrachtet. Schließlich wird die JavaScript-Engine es finden und wiederverwenden. Das ist hier etwas kompliziert zu erklären. Einfach ausgedrückt: Beim Schreiben von Code gibt es einige Objektreferenzbeziehungen. Sie können vom unteren Ende der Wurzel aus suchen und werden schließlich einige Objekte entsprechend der Referenzbeziehung finden. Wenn jedoch festgestellt wird, dass die Pfade zu diesen Objekten zerstört oder recycelt werden, können sie derzeit nicht wiedergefunden werden. Sie werden als Müll betrachtet und können schließlich vom Müllbereinigungsmechanismus recycelt werden. 4. Einführung in den GC-AlgorithmusGC kann als Abkürzung für den Garbage Collection-Mechanismus verstanden werden. Wenn GC arbeitet, kann es einige Garbage-Objekte im Speicher finden, den Speicherplatz freigeben und ihn recyceln, sodass nachfolgender Code diesen Teil des Speicherplatzes weiterhin verwenden kann. Bezüglich der Dinge, die in GC als Müll betrachtet werden können, gibt es zwei kleine Standards. Erstens muss man es aus der Perspektive der Programmanforderungen betrachten. Wenn bestimmte Daten nach der Verwendung im Kontext nicht mehr benötigt werden, können sie als Müll behandelt werden. Beispielsweise wird der Name im folgenden Code nach Abschluss des Funktionsaufrufs nicht mehr benötigt. Aus Sicht der Nachfrage sollte er daher als Müll recycelt werden. Ob es recycelt wurde oder nicht, wollen wir jetzt nicht diskutieren. Funktion func() { Name = "yd"; return `${name} ist ein Programmierer` } Funktion() Die zweite Situation besteht darin, zu prüfen, ob während der aktuellen Programmausführung auf die Variable verwiesen werden kann. Beispielsweise platziert der folgende Code immer noch einen Namen innerhalb der Funktion, aber dieses Mal wird ein Schlüsselwort zum Deklarieren der Variable hinzugefügt. Mit diesem Schlüsselwort kann nach Beendigung des Funktionsaufrufs nicht mehr auf den Namen im externen Raum zugegriffen werden. Wenn Sie ihn also nicht finden können, können Sie ihn tatsächlich als Müll betrachten. Funktion func() { konstanter Name = "yd"; return `${name} ist ein Programmierer` } Funktion() Nachdem wir über GC gesprochen haben, sprechen wir nun über den GC-Algorithmus. Wir wissen bereits, dass GC tatsächlich ein Mechanismus ist. Der darin enthaltene Garbage Collector kann bestimmte Recyclingarbeiten ausführen. Der Kern der Arbeit besteht darin, Müll zu finden, um Speicherplatz freizugeben und Speicherplatz zu recyceln. Dieser Prozess umfasst mehrere Aktionen: Platz finden, Platz freigeben und Platz zurückgewinnen. In einer solchen Reihe von Prozessen muss es verschiedene Möglichkeiten geben. Der GC-Algorithmus kann als eine Art Regel verstanden werden, die der Garbage Collector im Arbeitsprozess befolgt, wie einige mathematische Berechnungsformeln. Zu den gängigen GC-Algorithmen gehört die Referenzzählung, mit der anhand einer Zahl bestimmt werden kann, ob das aktuelle Objekt Müll ist. Mark Sweep kann aktiven Objekten Markierungen hinzufügen, wenn GC daran arbeitet, zu bestimmen, ob es sich um Müll handelt. Mark-Sweep ist Mark-Sweep sehr ähnlich, außer dass beim anschließenden Recyclingvorgang einige Dinge anders gemacht werden können. Generationsübergreifendes Recycling, der im V8 verwendete Recyclingmechanismus. 5. ReferenzzählalgorithmusDie Kernidee des Referenzzählalgorithmus besteht darin, die Anzahl der Referenzen auf das aktuelle Objekt intern über einen Referenzzähler aufrechtzuerhalten, um zu bestimmen, ob der Referenzwert des Objekts 0 ist und ob es sich um ein Müllobjekt handelt. Wenn dieser Wert 0 ist, beginnt GC zu arbeiten und recycelt den Objektspeicherort und gibt ihn frei. Die Existenz von Referenzzählern kann zu Unterschieden in der Ausführungseffizienz zwischen der Referenzzählung und anderen GC-Algorithmen führen. Eine Änderung des Referenzwerts bedeutet, dass der Referenzzähler bei einer Änderung der Referenzbeziehung eines Objekts den dem aktuellen Objekt entsprechenden Referenzwert aktiv ändert. Wenn es im Code beispielsweise einen Objektraum gibt und ein Variablenname darauf zeigt, ist der Wert zu diesem Zeitpunkt +1. Wenn ein anderes Objekt darauf zeigt, ist es wieder +1. Wenn es abnimmt, ist es -1. Wenn die Referenznummer 0 ist, beginnt GC sofort damit, den aktuellen Objektspeicherplatz wiederzuverwenden. Lassen Sie uns anhand eines einfachen Codes die Situation veranschaulichen, in der sich die Referenzbeziehung ändert. Definieren Sie zunächst einige einfache Benutzervariablen, behandeln Sie sie wie normale Objekte und definieren Sie dann eine Array-Variable, um die Altersattributwerte mehrerer Objekte im Array zu speichern. Definieren Sie eine weitere Funktion und definieren Sie im Funktionskörper mehrere Variablenwerte num1 und num2. Beachten Sie, dass hier kein const vorhanden ist. Aufrufen einer Funktion auf der äußeren Ebene. const Benutzer1 = {Alter: 11}; const Benutzer2 = {Alter: 22}; const user3 = {Alter: 33}; const nameList = [Benutzer1.Alter, Benutzer2.Alter, Benutzer3.Alter,]; Funktion fn() { Zahl1 = 1; Zahl2 = 2; } fn(); Zunächst werden Sie aus globaler Sicht feststellen, dass sich user1, user2, user3 und nameList direkt unter dem Fenster befinden. Gleichzeitig werden die in der fn-Funktion definierten num1 und num2 auch unter dem Fensterobjekt bereitgestellt, da keine Schlüsselwörter festgelegt sind. Zu diesem Zeitpunkt sind die Referenzzähler dieser Variablen definitiv nicht 0. Deklarieren Sie dann num1 und num2 direkt mit Schlüsselwörtern in der Funktion, was bedeutet, dass die aktuellen Werte num1 und num2 nur innerhalb des Bereichs wirksam werden können. Sobald der Funktionsaufruf abgeschlossen ist, können num1 und num2 daher nicht vom externen globalen Standort aus gefunden werden und die Referenzzähler für num1 und num2 werden auf 0 zurückgesetzt. In diesem Moment beginnt GC, solange es 0 ist, sofort zu arbeiten und recycelt num1 und num2 als Müll. Das heißt, nachdem die Funktion ausgeführt wurde, wird der interne Speicherplatz wiederverwendet. const Benutzer1 = {Alter: 11}; const Benutzer2 = {Alter: 22}; const user3 = {Alter: 33}; const nameList = [Benutzer1.Alter, Benutzer2.Alter, Benutzer3.Alter,]; Funktion fn() { Konstante Nummer1 = 1; Konstante Nummer2 = 2; } fn(); Schauen wir uns dann andere an, etwa user1, user2, user3 und nameList. Da userList nur auf die oben genannten drei Objektbereiche verweist, werden die Bereiche in user1, user2 und user3 auch nach einmaliger Ausführung des Skripts weiterhin referenziert. Daher sind die Referenzzähler zu diesem Zeitpunkt nicht 0 und werden nicht als Müll recycelt. Dies ist das Grundprinzip, das bei der Implementierung des Referenzzählalgorithmus befolgt wird. Die einfache Zusammenfassung besteht darin, sich auf den Wert des Referenzzählers des aktuellen Objekts zu verlassen, um zu bestimmen, ob er 0 ist, und dadurch festzustellen, ob es sich um ein Müllobjekt handelt. 1. Vor- und Nachteile der ReferenzzählungDie Vorteile des Referenzzählalgorithmus lassen sich in zwei Teile zusammenfassen. Erstens recycelt die Referenzzählregel Müll sofort, wenn er gefunden wird, da sie anhand dessen, ob der aktuelle Referenzzähler 0 ist, bestimmen kann, ob es sich bei dem Objekt um Müll handelt. Wenn ja, kann die Freigabe sofort erfolgen. Der zweite Grund besteht darin, dass der Referenzzählalgorithmus Programmpausen minimieren kann. Während der Ausführung der Anwendung wird zwangsläufig Speicher verbraucht. Der Speicher der aktuellen Ausführungsplattform muss eine Obergrenze haben, sodass der Speicher auf jeden Fall voll sein wird. Da der Referenzzählalgorithmus immer die Objekte mit einem Speicherreferenzwert von 0 überwacht, findet der Referenzzähler im Extremfall, wenn er feststellt, dass der Speicher fast voll ist, sofort den Objektspeicherplatz mit dem Wert 0 und gibt ihn frei. Dadurch wird sichergestellt, dass der aktuelle Speicher nie voll ist, man spricht von einer Reduzierung der Programmpausen. Auch für die Nachteile der Referenzzählung gibt es zwei Erklärungen. Der erste Grund besteht darin, dass der Referenzzählalgorithmus keine Möglichkeit hat, den Speicherplatz von Objekten mit zirkulären Referenzen zurückzugewinnen. Der folgende Codeausschnitt zeigt, was ein zirkuläres Referenzobjekt ist. Definieren Sie eine normale Funktion fn und definieren Sie zwei Variablen innerhalb des Funktionskörpers, die Objekte obj1 und obj2. Lassen Sie obj1 ein Namensattribut haben, das auf obj2 zeigt, und lassen Sie obj2 ein Attribut haben, das auf obj1 zeigt. Am Ende der Funktion gibt return ein normales Zeichen zurück. Dies hat natürlich keine praktische Bedeutung und ist nur ein Test. Rufen Sie dann die Funktion auf der äußersten Ebene auf. Funktion fn() { const obj1 = {}; const obj2 = {}; obj1.name = obj2; obj2.name = obj1; returniere „yd ist ein Programmierer“; } Dann ist die nächste Analyse immer noch dieselbe. Nachdem die Funktion ausgeführt wurde, muss der darin enthaltene Speicherplatz in das Speicherplatzrecycling einbezogen werden. Beispielsweise wird auf obj1 und obj2 nicht mehr global verwiesen, sodass ihre Referenzzähler zu diesem Zeitpunkt 0 sein sollten. Zu diesem Zeitpunkt tritt jedoch ein Problem auf. Sie werden feststellen, dass GC, wenn es obj1 löschen möchte, feststellt, dass obj2 ein Attribut hat, das auf obj1 verweist. Mit anderen Worten: Obwohl gemäß den vorherigen Regeln obj1 und obj2 nicht im globalen Bereich gefunden werden können, besteht innerhalb des Bereichs offensichtlich eine gegenseitige Führungsbeziehung zwischen ihnen. In diesem Fall sind die Referenzzählerwerte darauf nicht 0 und GC hat keine Möglichkeit, diese beiden Bereiche zurückzugewinnen. Dies führt außerdem zu einer Verschwendung von Speicherplatz, da es zu sogenannten Zirkelverweisen zwischen Objekten kommt. Dies ist auch ein Problem beim Referenzzählalgorithmus. Das zweite Problem besteht darin, dass der Referenzzählalgorithmus mehr Zeit in Anspruch nimmt, da der aktuelle Referenzzähler eine Wertänderung beibehalten muss. In diesem Fall muss ständig überwacht werden, ob der Referenzwert des aktuellen Objekts geändert werden muss. Das Ändern des Werts eines Objekts nimmt Zeit in Anspruch. Wenn sich im Speicher mehrere Objekte befinden, die geändert werden müssen, dauert es sehr lange. Daher ist der Zeitaufwand des Referenzzählalgorithmus im Vergleich zu anderen GC-Algorithmen größer. 6. Mark-and-Sweep-AlgorithmusIm Vergleich zur Referenzzählung ist das Prinzip des Mark-and-Sweep-Algorithmus einfacher und kann auch einige entsprechende Probleme lösen. Es wird häufig in V8 verwendet. Die Kernidee des Mark-and-Sweep-Algorithmus besteht darin, den gesamten Garbage Collection-Vorgang in zwei Phasen zu unterteilen: Die erste Phase durchläuft alle Objekte und findet dann aktive Objekte zum Markieren. Die Aktivität ist dieselbe wie bei den zuvor erwähnten erreichbaren Objekten. In der zweiten Phase werden alle Objekte weiterhin durchlaufen und nicht markierte Objekte gelöscht. Es ist zu beachten, dass die im ersten Schritt gesetzte Markierung auch im zweiten Schritt gelöscht wird, damit der GC beim nächsten Mal normal arbeiten kann. Auf diese Weise kann der aktuelle Garbage Space durch zwei Durchlaufverhalten zurückgefordert und schließlich zur Wartung an die entsprechende freie Liste übergeben werden, und nachfolgender Programmcode kann verwendet werden. Dies ist das Grundprinzip des Mark-and-Sweep-Algorithmus. Tatsächlich besteht er aus zwei Operationen: Die erste ist das Markieren und die zweite das Löschen. Hier ist ein Beispiel. Deklarieren Sie zunächst global drei erreichbare Objekte A, B und C. Nachdem Sie diese drei erreichbaren Objekte gefunden haben, werden Sie feststellen, dass sich darunter einige Unterreferenzen befinden. Hier ist der Mark-Sweep-Algorithmus sehr leistungsstark. Wenn festgestellt wird, dass sich darunter Kinder befinden, und sogar Kinder unter den Kindern, wird rekursiv weiter nach erreichbaren Objekten gesucht. Beispielsweise sind D und E Kindreferenzen von A bzw. C und werden ebenfalls als erreichbar markiert. Hier gibt es zwei Variablen a1 und b1. Sie befinden sich im lokalen Gültigkeitsbereich der Funktion. Nach der Ausführung im lokalen Gültigkeitsbereich wird der Speicherplatz wiederverwendet. Daher können a1 und b1 nicht in der globalen Kette gefunden werden. Zu diesem Zeitpunkt wird der GC-Mechanismus denken, dass es sich um ein Müllobjekt handelt, und es nicht markieren. Schließlich wird es recycelt, wenn GC funktioniert. konstant A = {}; Funktion fn1() { Konstante D = 1; ANZEIGE = D; } fn1(); Konstante B; const C = {}; Funktion fn2() { Konstante E = 2; AE = E; } fn2(); Funktion fn3() { Konstante a1 = 3; Konstante b1 = 4; } fn3(); Dies ist die Bezeichnung für die Markierungsphase und die Sweep-Phase im Mark-and-Sweep-Prozess und was jede Phase bewirkt. Einfaches Aufräumen lässt sich in zwei Schritte unterteilen. Im ersten Schritt werden alle erreichbaren Objekte gesucht. Bei einer Referenzhierarchie erfolgt die Suche rekursiv. Nach Abschluss der Suche werden diese erreichbaren Objekte markiert. Nachdem die Markierung abgeschlossen ist, beginnt die zweite Phase mit der Bereinigung. Dabei werden die nicht markierten Objekte gefunden und die in der ersten Phase vorgenommenen Markierungen gelöscht. Damit ist eine Garbage Collection abgeschlossen. Beachten Sie dabei, dass der wiederhergestellte Speicherplatz ggf. direkt auf eine Freiliste gesetzt wird. Um Ihnen das weitere Vorgehen zu erleichtern, können Sie sich hier direkt für einen Platz bewerben. 1. Vor- und Nachteile des Mark-Sweep-AlgorithmusIm Vergleich zur Referenzzählung hat das Mark-Sweeping einen großen Vorteil: Es kann den Recyclingvorgang von zirkulären Objektreferenzen lösen. Beim Schreiben von Code können Sie global erreichbare Objekte wie A, B und C definieren. Es kann auch einige lokale Funktionsbereiche geben, wie z. B. das Definieren von a1 und b1 innerhalb einer Funktion und das Zulassen, dass diese sich gegenseitig referenzieren. konstant A = {}; Konstante B; const C = {}; Funktion fn() { a1 = {}; const b1 = {}; a1.Wert = b1; b1.Wert = a1; } fn(); Nach Beendigung des Funktionsaufrufs muss der darin enthaltene Speicherplatz freigegeben werden. In diesem Fall verlieren die Variablen in ihrem lokalen Speicherplatz nach Beendigung eines Funktionsaufrufs ihre Verbindung zum globalen Bereich. Zu diesem Zeitpunkt kann auf a1 und b1 unter der globalen Wurzel nicht zugegriffen werden und es handelt sich um nicht erreichbare Objekte. Nicht erreichbare Objekte können in der Markierungsphase nicht markiert werden und werden in der zweiten Recyclingphase direkt freigegeben. Dies ist, was Mark-Sweep tun kann, aber beim Referenzzählen endet der Funktionsaufruf und es gibt keine Möglichkeit, global darauf zuzugreifen. Da das aktuelle Beurteilungskriterium jedoch darin besteht, ob die Referenznummer 0 ist, gibt es in diesem Fall keine Möglichkeit, die Leerzeichen a1 und b1 freizugeben. Dies ist natürlich der größte Vorteil des Mark-and-Sweep-Algorithmus gegenüber dem Referenzzählalgorithmus. Gleichzeitig weist der Mark-and-Sweep-Algorithmus auch einige Nachteile auf. Wenn beispielsweise eine Speichersituation simuliert und von der Wurzel aus gesucht wird, befindet sich unten ein erreichbares Objekt A und auf der linken und rechten Seite gibt es die Bereiche B und C, die von der Wurzel aus nicht direkt durchsucht werden können. In diesem Fall wird während der zweiten Runde der Löschvorgänge der Speicherplatz, der B und C entspricht, direkt zurückgefordert. Der freigegebene Speicherplatz wird dann der Freiliste hinzugefügt und nachfolgende Programme können direkt eine entsprechende Speicherplatzadresse aus der Freiliste zur Nutzung beantragen. Diese Situation weist ein Problem auf. Funktion fn() { const B = "zwei"; } fn(); const A = 'vier Zeichen'; Funktion fn2() { Konstante C = "a"; } fn2(); Wir gehen beispielsweise davon aus, dass jeder Speicherplatz aus zwei Teilen besteht. Einer wird zum Speichern einiger Metadaten des Speicherplatzes verwendet, beispielsweise seiner Größe und Adresse. Dieser Teil wird als Header bezeichnet. Es gibt auch einen Teil, der speziell zum Speichern von Daten verwendet wird und als Domäne bezeichnet wird. Die Bereiche B und C setzen voraus, dass das Objekt B Platz für 2 Wörter und das Objekt C Platz für 1 Wort hat. In diesem Fall wird zwar recycelt, aber insgesamt scheint der Platz für 3 Wörter freigegeben zu werden, diese werden jedoch durch das A-Objekt geteilt. Daher sind sie nach Abschluss der Veröffentlichung immer noch verstreut, d. h. die Adressen sind nicht kontinuierlich. Dies ist sehr wichtig. Die Adressgröße des Speicherplatzes, für den Sie sich später bewerben möchten, beträgt genau 1,5 Wörter. Wenn Sie in diesem Fall direkt nach dem von B freigegebenen Speicherplatz suchen, werden Sie feststellen, dass zu viel davon vorhanden ist, da noch 0,5 vorhanden sind. Wenn Sie direkt nach dem von C freigegebenen Speicherplatz suchen, werden Sie feststellen, dass nicht genug davon vorhanden ist, da nur 1 vorhanden ist. Dies bringt uns zum größten Problem des Mark-and-Sweep-Algorithmus: der Speicherfragmentierung. Die sogenannte Speicherplatzfragmentierung besteht darin, dass die Adressen der aktuell recycelten Müllobjekte nicht kontinuierlich sind. Aufgrund dieser Diskontinuität werden sie nach dem Recycling in verschiedene Ecken verstreut. Wenn Sie sie später verwenden möchten und der neu generierte Speicherplatz zufällig ihrer Größe entspricht, können Sie sie direkt verwenden. Ist die Menge einmal zu groß oder zu klein, ist das Produkt für den Gebrauch nicht geeignet. Dies sind die Vor- und Nachteile des Mark-and-Sweep-Algorithmus. Einfach ausgedrückt besteht der Vorteil darin, dass er das Problem nicht wiederverwendbarer Zirkelverweise lösen kann, und der Nachteil besteht darin, dass er zu einer Raumfragmentierung führt und die Raumnutzung nicht maximieren kann. 7. Mark-Sweep-AlgorithmusDer Mark-Collation-Algorithmus wird in V8 häufig verwendet. Sehen wir uns an, wie er implementiert wird. Zunächst einmal denken wir, dass der Mark-Sweep-Algorithmus eine erweiterte Operation von Mark-Sweep ist. In der ersten Phase sind sie genau gleich. Sie durchlaufen alle Objekte und markieren dann die erreichbaren aktiven Objekte. In der zweiten Löschphase gibt das Mark Sweeping den Speicherplatz unmarkierter Müllobjekte direkt zurück, während die Mark Compaction vor dem Löschen Komprimierungsvorgänge ausführt und die Positionen der Objekte so verschiebt, dass ihre Adresse kontinuierlich ist. Unter der Annahme, dass viele aktive und inaktive Objekte sowie etwas freier Speicherplatz vor dem Recycling vorhanden sind, werden beim Ausführen des Markierungsvorgangs alle aktiven Objekte markiert und anschließend der Sortiervorgang durchgeführt. Beim Sortieren handelt es sich eigentlich um eine Positionsänderung. Dabei werden zunächst die aktiven Objekte verschoben, um sie in der Adressierung fortlaufend zu machen. Anschließend wird der gesamte Bereich rechts vom aktiven Objekt recycelt, was gegenüber dem Mark-Sweep-Algorithmus klare Vorteile bietet. Da es im Speicher nicht viele verstreute kleine Bereiche gibt, ist der zurückgewonnene Speicherplatz grundsätzlich kontinuierlich. Dadurch können Sie den frei gewordenen Speicherplatz bei der nachfolgenden Verwendung optimal nutzen. Bei diesem Prozess handelt es sich um den Mark-Compact-Algorithmus, der zusammen mit Mark-Sweep häufige GC-Operationen in der V8-Engine implementiert. 8. AusführungszeitpunktDer erste ist der Referenzzähler, mit dem Müllobjekte rechtzeitig recycelt werden können. Solange der Wert 0 ist, findet GC diesen Speicherplatz sofort zum Recycling und zur Freigabe. Genau aufgrund dieser Funktion kann die Referenzzählung Programmstaus minimieren, denn solange der Speicherplatz fast voll ist, arbeitet der Garbage Collector daran, den Speicher freizugeben, sodass im Speicherplatz immer etwas freier Speicherplatz vorhanden ist. Mark Sweep kann Müllobjekte nicht sofort recyceln, und das aktuelle Programm funktioniert tatsächlich nicht mehr, wenn es auf Löschen wechselt. Auch wenn im ersten Schritt Müll gefunden wird, erfolgt keine Wiederverwertung, bis er im zweiten Schritt beseitigt wird. Durch die Markierungskomprimierung können Müllobjekte auch nicht sofort wiederhergestellt werden. 9. V8-MotorWie wir alle wissen, ist die V8-Engine die gängigste JavaScript-Ausführungs-Engine auf dem Markt. Der im täglichen Leben verwendete Chrome-Browser und die NodeJavaScript-Plattform verwenden diese Engine zur Ausführung von JavaScript-Code. Der Grund, warum JavaScript auf diesen beiden Plattformen effizient ausgeführt werden kann, liegt gerade in der Existenz von V8. Der Grund für die Schnelligkeit von V8 liegt neben einem hervorragenden Speicherverwaltungsmechanismus auch in der Just-in-Time-Kompilierung. Bisher mussten viele JavaScript-Engines den Quellcode vor der Ausführung in Bytecode umwandeln, aber V8 kann den Quellcode in Maschinencode übersetzen, der direkt ausgeführt werden kann. Die Ausführungsgeschwindigkeit ist also sehr schnell. Ein weiteres wichtiges Merkmal von V8 ist, dass sein Speicher eine Obergrenze hat. Unter einem 64-Bit-Betriebssystem überschreitet die Obergrenze 1,5 G nicht und unter einem 32-Bit-Betriebssystem überschreitet der Wert 800 M nicht. Warum verfolgt V8 diesen Ansatz? Die Gründe dafür können grundsätzlich aus zwei Aspekten erklärt werden. Erstens ist V8 selbst für Browser konzipiert, sodass die vorhandene Speichergröße für die Verwendung ausreicht. Darüber hinaus legt der in V8 implementierte Speicherbereinigungsmechanismus fest, dass die Übernahme einer solchen Einstellung sehr sinnvoll ist. Da der Beamte einen Test durchgeführt hat, benötigt V8, wenn der Garbage-Speicher 1,5 G erreicht, nur 50 ms, um den inkrementellen Markierungsalgorithmus für die Garbage Collection zu verwenden, und es dauert 1 s, um Garbage mit der nicht-inkrementellen Markierungsmethode zu sammeln. Aus Sicht der Benutzererfahrung ist 1 s bereits eine sehr lange Zeit, daher werden 1,5 G als Grenze verwendet. 1. Strategie zur SpeicherbereinigungBei der Verwendung des Programms werden viele Daten verwendet, und die Daten können in primitive Daten und Objekttypdaten unterteilt werden. Die grundlegenden Rohdaten werden durch die Programmiersprache selbst gesteuert. Daher bezieht sich das hier erwähnte Recycling hauptsächlich auf die Objektdaten, die im Heap-Bereich verbleiben, sodass dieser Prozess untrennbar mit Speichervorgängen verbunden ist. V8 übernimmt die Idee des Generationsrecyclings und unterteilt den Speicherplatz gemäß bestimmten Regeln in zwei Kategorien: den Speicherbereich der neuen Generation und den Speicherbereich der alten Generation. Nach der Klassifizierung wird der effizienteste GC-Algorithmus für verschiedene Generationen verwendet, um unterschiedliche Objekte zu recyceln. Dies bedeutet, dass beim V8-Recycling viele GC-Algorithmen zum Einsatz kommen. Zunächst muss der Algorithmus zur generationellen Wiederherstellung verwendet werden, da eine Unterteilung in Generationen erforderlich ist. Als nächstes wird der Raumreplikationsalgorithmus verwendet. Darüber hinaus werden auch Mark-Sweep und Mark-Compact verwendet. Zur Verbesserung der Effizienz wird abschließend erneut die Markierungsinkrementierung verwendet. 2. Recycling von Objekten der neuen GenerationAls Erstes muss die Speicherzuweisung innerhalb von V8 erläutert werden. Da es auf der Idee der Generationsmüllsammlung basiert, ist der Speicherplatz in zwei Teile in V8 unterteilt. Der Platz links wird verwendet, um Objekte der neuen Generation zu speichern, und der Platz rechts wird verwendet, um Objekte der alten Generation zu speichern. Der Objektraum der neuen Generation verfügt über bestimmte Einstellungen. Die neue Generation von Objekten bezieht sich tatsächlich auf diejenigen mit einer kürzeren Überlebenszeit. Zum Beispiel gibt es im aktuellen Code einen lokalen Umfang, und die Variablen im Bereich werden nach Abschluss der Ausführung recycelt. Relativ gesehen bezieht sich die neue Generation auf diese variablen Objekte mit einer kürzeren Überlebenszeit. Die zum Recycling von Objekten der neuen Generation verwendeten Algorithmen sind hauptsächlich Kopieralgorithmus und Mark-Compact-Algorithmus. Wenn der Code ausgeführt wird, werden bei diesen beiden Leerzeichen, wenn der Speicherplatz angewendet werden muss, alle variablen Objekte zuerst aus dem aus dem Raum zugewiesenen Variablenobjekten zugeordnet. Das heißt, ist während dieses Vorgangs im Leerlauf. Zu diesem Zeitpunkt werden Mark und Sortierung verwendet, um die aus dem Raum aus dem Raum zu markieren, die aktiven Objekte zu finden und dann Sortiervorgänge zu verwenden, um ihre Positionen kontinuierlich zu gestalten, sodass fragmentierter Raum später nicht erzeugt wird. Nach Abschluss dieser Vorgänge wird das aktive Objekt in den Raum kopiert, was bedeutet, dass das aktive Objekt im From -Raum eine Sicherung hat und das Recycling zu diesem Zeitpunkt berücksichtigt werden kann. Recycling ist auch sehr einfach. Zusammenfassend lässt sich sagen, dass der Speicherbereich der neuen Generation von Objekten in zwei gleichgroße Räume unterteilt ist. Wenn der GC -Mechanismus ausgelöst wird, werden alle aktiven Objekte gefunden, sortiert und zum Raum kopiert. Nachdem die Kopie abgeschlossen ist, lassen wir von und aus dem Austausch von Raum (dh aus Austausch Namen), das Original, das von und das Original von wird zu. Dadurch wird die Veröffentlichung und Wiederherstellung des Raums abgeschlossen. Im Folgenden beschreibt die Details des Prozesses. Das erste, was in diesem Prozess in den Sinn kommt, ist, dass beim Kopieren der Raum, auf das ein variabler Objekt angezeigt wird, auch im aktuellen Objekt der alten Generation angezeigt wird. Zu diesem Zeitpunkt erfolgt eine Operation namens Promotion, bei der die Objekte der neuen Generation auf die alte Generation zur Speicherung verschoben werden sollen. Wenn Sie den Werbevorgang auslösen, gibt es im Allgemeinen zwei Kriterien. Zu diesem Zeitpunkt kann es für die Lagerung in den Speicherbereich der alten Generation kopiert werden. Wenn die Nutzungsrate des TO -Raums während des aktuellen Kopiervorgangs 25% überschreitet, müssen alle aktiven Objekte zur Speicherung auf die alte Generation verschoben werden. Warum 25%wählen? Tatsächlich ist es leicht zu verstehen, denn wenn der Recycling -Operation in Zukunft durchgeführt wird, werden die From -Raum und der Raum schließlich ausgetauscht. Das heißt, der Vorherige wird daraus werden, und der vorherige wird zu. Die einfache Erklärung ist, dass der Raum für neue Objekte möglicherweise nicht ausreicht, wenn die Nutzungsrate des TO -Raums eine bestimmte Grenze überschreitet, wenn sie in Zukunft verwendet wird, sodass es eine solche Grenze geben wird. Um es einfach zusammenzufassen, ist der aktuelle Speicher in zwei Teile unterteilt, ein Teil wird verwendet, um die Objekte der neuen Generation zu speichern. Anschließend können Sie den Mark-and-Snap-Algorithmus verwenden, um die aktiven Objekte im From-Raum zu markieren und zu sortieren und sie dann zum Raum zu kopieren. Tauschen Sie schließlich die Zustände der beiden Räume aus, und der Raumfreisetzungsvorgang wird abgeschlossen. 3. Objekte der alten Generation recycelnDie Objekte der alten Generation werden auf der rechten Seite des Speicherplatzes gespeichert. Objekte der alten Generation beziehen sich auf Objekte, die eine längere Überlebenszeit haben, z. Die drei Hauptalgorithmen, die für die Müllsammlung in der alten Generation verwendet werden, sind Mark-Sweep, Mark-Compact und Incremental Mark. Der Mark-Sweep-Algorithmus wird hauptsächlich zum Freigeben und Recyceln von Mülltonnen verwendet. Offensichtlich wird es an dieser Stelle einige Raumfragmentierungsprobleme geben. Denn im Vergleich zu Weltraumabfällen ist seine Verbesserungsgeschwindigkeit sehr offensichtlich. Unter welchen Umständen wird der Mark-Sweep-Algorithmus verwendet? Wenn der Inhalt der neuen Generation in die alte Generation verschoben werden muss und der Raum im Speicherbereich der alten Generation zu diesem Zeitpunkt nicht ausreicht, um die von dem Speicherbereich der neuen Generation verschobenen Objekte zu speichern. In diesem Fall wird die Mark -Verdichtung ausgelöst, um einen Teil des vorherigen Sperrraums zurückzugewinnen, so dass das Programm mehr Platz zur Nutzung hat. Schließlich wird eine inkrementelle Markierung verwendet, um die Effizienz des Recyclings zu verbessern. Hier vergleichen wir die Müllsammlung der neuen und alten Generationen. Die neue Generation der Garbage Collection ist eher dem Austausch von Raum für die Zeit, da ein Kopieralgorithmus verwendet wird, was bedeutet, dass immer ein freier Speicherplatz darin liegt. Da der Raum des Speicherbereichs der neuen Generation selbst sehr klein ist, ist der zugewiesene Raum noch kleiner. Warum nicht diesen Eins-zu-zwei-Ansatz im Recyclingprozess von Objekten der alten Generation anwenden? Da der Speicherplatz der alten Generation relativ groß ist, werden Hunderte von Megabyte des Raums verschwendet, wenn er in zwei unterteilt ist, was zu extravagant ist. Das zweite ist, dass im Speicherbereich der alten Generation mehr Objektdaten gespeichert sind, sodass die Zeit im Zuordnung sehr lang verbraucht ist. Wie optimiert der inkrementelle Markierungsalgorithmus, der zuvor erwähnt wurde, die Abbetriebnahme des Müllsammers? Erstens ist es in zwei Teile unterteilt, eine Programmausführung und der andere ist die Müllsammlung. Zunächst ist klar, dass die Ausführung des aktuellen JavaScript -Programms, dh bei der Funktionsweise der Müllsammlung, eine Lückezeit, beispielsweise nach Abschluss der Programmausführung, die Ausführung von Müllsammlungsvorgängen vorliegt. Der sogenannte Mark-Increment-Vorgang bedeutet einfach, den gesamten Müllabfassungsvorgang in mehrere kleine Schritte aufzuteilen und die gesamte Sammlung in Gruppen zu vervollständigen, wodurch der zuvor in einem Los abgeschlossene Müllabfuhrbetrieb ersetzt wird. Der Hauptvorteil davon ist, dass die Müllsammlung und die Programmausführung abwechselnd abgeschlossen werden können, was zu einem angemesseneren Zeitverbrauch führt. Dies vermeidet die Situation in der Vergangenheit, in der die Müllsammlung nicht während des Programms durchgeführt werden kann, und das Programm kann nicht weiter ausgeführt werden, wenn die Müllsammlung durchgeführt wird. Nehmen wir ein einfaches Beispiel, um das Implementierungsprinzip der inkrementellen Markierung zu veranschaulichen. Wenn das Programm zum ersten Mal ausgeführt wird, ist die Müllsammlung nicht erforderlich. Die Markierung ist während des Durchlaufverfahrens erforderlich. Dann können Sie anhalten und das Programm für eine Weile ausführen lassen. Wenn das Programm seit einiger Zeit ausgeführt wurde und die GC -Maschine weiterhin den zweiten Markierungsschritt durchführt, beispielsweise wenn auch einige Kinderelemente erreicht werden, markieren Sie sie weiter. Nach einer Runde der Markierung wird der GC gestoppt und die Programmausführung wird fortgesetzt, dh Markier- und Programmausführung werden abwechselnd durchgeführt. Nach Abschluss des endgültigen Markierungsbetriebs wird die Müllsammlung in diesem Zeitraum abgeschlossen. Obwohl das Programm viele Male inne, ist die größte Müllsammlung des gesamten V8, wenn das Speicher 1,5 g erreicht, und die Müllsammlungszeit mit nicht-inkrementeller Markierung ist 1S nicht überschreitet, sodass die Unterbrechung des Programms hier angemessen ist. Darüber hinaus wird die bisher lange Pausezeit in kleinere Segmente in maximalem Maße aufgeteilt, sodass die Benutzererfahrung optimierter wird. V8 Müllsammlung ZusammenfassungZunächst sollten Sie wissen, dass die V8 -Engine die aktuelle Mainstream -JavaScript -Engine ist. Die zweite wird durch den internen Müllmechanismus bestimmt. V8 übernimmt die Idee des Generationsrecyclings und unterteilt die Erinnerung in die neue Generation und die alte Generation. Die neue Generation und die alte Generation unterscheiden sich in Bezug auf Raum- und Speicherdatentyp. Die neue Generation verfügt über einen Platz von 32 m unter einem 64-Bit-Betriebssystem und 16 m Platz unter einem 32-Bit-System. V8 verwendet verschiedene GC-Algorithmen für verschiedene Generationen von Objekten, um die Müllabfuhrvorgänge abzuschließen. 10. Einführung in LeistungstoolsDer Zweck von GC ist es, den Speicherraum während des Ausführens des Programms in einem gutartigen Zyklus zu verwenden. Die Grundlage des sogenannten tugendhaften Zyklus besteht darin, dass Entwickler beim Schreiben von Code den Speicherplatz vernünftigerweise zuweisen. Da ECMascript den Programmierern jedoch die entsprechende API für den Betriebsspeicherraum nicht zur Verfügung stellt, scheint es nicht bekannt, ob dies vernünftig ist, da alles automatisch von GC durchgeführt wird. Wenn Sie feststellen möchten, ob die Speicherverwendung des gesamten Prozesses angemessen ist, müssen Sie einen Weg finden, um die Speicheränderungen jederzeit im Auge zu behalten. Daher gibt es ein solches Tool, mit dem Entwickler mehr Überwachungsmethoden ermöglichen und Entwicklern helfen können, den Speicherplatz während des Programms zu überwachen. Durch die Verwendung der Leistung können Sie die Änderungen des Speichers während der Programmausführung in Echtzeit überwachen. Auf diese Weise finden Sie bei einem Problem mit dem Speicher des Programms direkt einen Weg, um den problematischen Codeblock zu finden. Schauen wir uns die grundlegenden Schritte für die Verwendung des Performance -Tools an. Öffnen Sie zuerst den Browser und geben Sie die URL in die Adressleiste ein. Es wird nicht empfohlen, sofort nach dem Eintritt auf die Adresse zugreifen zu können, da Sie den ersten Rendering -Prozess aufzeichnen möchten. Öffnen Sie also einfach die Schnittstelle und geben Sie die URL ein. Öffnen Sie als Nächstes das Entwickler -Tools -Panel (F12) und wählen Sie die Leistung. Schalten Sie die Aufzeichnungsfunktion ein und dann können Sie auf die Ziel -URL zugreifen. Führen Sie einige Operationen auf dieser Seite aus und hören Sie nach einer Weile auf. Sie können einen Bericht erhalten, in dem Sie Speicherinformationen analysieren können. Nach der Aufnahme werden einige Diagramme angezeigt, und es gibt viele Informationen, die ziemlich kompliziert zu sein scheint. Hier konzentrieren wir uns hauptsächlich auf Informationen zum Speicher, und es gibt eine Speicheroption (Speicher). Wenn es standardmäßig nicht überprüft wird, müssen Sie es überprüfen. Auf der Seite ist eine blaue Linie zu sehen. Dies ist die Änderung, die während des gesamten Vorgangs in meinem Gedächtnis auftritt. Wenn es irgendwo ein Problem gibt, können Sie es beispielsweise beobachten. 1. Manifestation von GedächtnisproblemenWenn es ein Problem mit dem Speicher des Programms gibt, welche spezifische Form wird es annehmen? Wenn die Schnittstelle eine verzögerte Belastung oder häufige Pausen aufweist, müssen wir zunächst sicherstellen, dass die Netzwerkumgebung in diesem Fall normal ist. Das heißt, es muss Code im Code geben, der dazu führt, dass der Speicher sofort explodiert. Dieser Code ist nicht für die Positionierung geeignet. Das zweite ist, wenn die Schnittstelle eine anhaltend schlechte Leistung hat, dh sie ist in diesem Fall nicht besonders einfach zu bedienen. Die sogenannte Speichererweiterung bedeutet, dass die aktuelle Schnittstelle, um die beste Nutzungsgeschwindigkeit zu erreichen, eine bestimmte Menge an Speicherplatz angewendet werden kann. Die Größe dieses Speicherplatzes übertrifft jedoch bei weitem die Größe, die das aktuelle Gerät selbst liefern kann. Wenn Sie einige Schnittstellen verwenden, wird der Vorgang in diesem Fall zu Beginn kein Problem, wenn Sie das Gefühl haben, dass die Glättung der Schnittstelle langsamer oder schlechter wird. Hier geht es um die Situation, in der die Anwendung während der Ausführung auf Speicherprobleme stößt. 2. verschiedene Möglichkeiten, den Speicher zu überwachenSpeicherprobleme können im Allgemeinen in drei Typen zusammengefasst werden: Speicherlecks, Speicherausdehnung und häufige Müllsammlung. Welche Standards sollten verwendet werden, um sie zu definieren? Ein Speicherleck ist tatsächlich eine kontinuierliche Erhöhung der Speicherverwendung, die leicht zu beurteilen ist. Wenn Sie feststellen, dass der Speicher ohne Rückgang des gesamten Vorgangs weiter zunimmt, bedeutet dies, dass der Programmcode ein Speicherleck enthält. Zu diesem Zeitpunkt sollten Sie das entsprechende Modul im Code finden. Die Speicherausdehnung ist relativ vage. Um festzustellen, ob es sich um ein Programmproblem oder ein Geräteproblem handelt, sollten Sie mehr Tests durchführen. Zu diesem Zeitpunkt finden Sie die Geräte, die bei Benutzern beliebt sind, und die Anwendung auf ihnen ausführen. Dies bedeutet, dass es ein Problem mit dem Programm selbst gibt, nicht mit dem Gerät. In diesem Fall müssen Sie in den Code zurückkehren und feststellen, wo das Speicherproblem auftritt. Die spezifischen Möglichkeiten zur Überwachung von Speicheränderungen stützen sich hauptsächlich auf Tools, die vom Browser bereitgestellt werden. Der vom Browser bereitgestellte Task -Manager kann die Speicheränderungen der aktuellen Anwendung während seiner Ausführung auf numerische Weise direkt anzeigen. Die zweite besteht darin, das Timing -Timing -Diagramm zu verwenden, um den Trend des gesamten Speichers während der Ausführung der Anwendung in Form von Zeitpunkten direkt vorzustellen. Darüber hinaus gibt es eine Funktion namens Heap -Snapshot im Browser, die speziell herausfinden kann, ob es einige getrennte DOMs in den Grenzflächenobjekten gibt, da die Existenz von getrennten DOMs eine Art Speicherleck ist. Wie Sie beurteilen können, ob es in der Schnittstelle eine häufige Müllsammlung gibt, müssen verschiedene Tools verwendet werden, um das aktuelle Speichertrenddiagramm zu erhalten, und es über einen bestimmten Zeitraum zu analysieren, um ein Urteil zu fällen. 3.. Task Manager überwacht den SpeicherWährend der Ausführung einer Webanwendung gibt es eine einfache Demo, die den Speichermanager verwenden kann, um die Speicheränderungen zu überwachen, wenn das Skript ausgeführt wird. Platzieren Sie ein Element in die Schnittstelle, fügen Sie ein Klickereignis hinzu und erstellen Sie ein Array mit sehr langer Länge, wenn das Ereignis ausgelöst wird. Dies führt zum Verbrauch des Speicherraums. <Text> <button id="btn">hinzufügen</button> <Skript> const obtn = document.getElementById ('btn'); obtn.onclick = function () { Sei arrlist = New Array (1000000) } </Skript> </body> Öffnen Sie nach Fertigstellung den Browser und führen Sie ihn in der oberen rechten Ecke aus und finden Sie den Task -Manager, um ihn zu öffnen. Zu diesem Zeitpunkt können Sie das aktuell ausführende Skript im Task-Manager finden. Die beiden Spalten von Interesse hier sind die Spalten Speicher und JavaScript -Speicher. Die erste Spalte des Speichers repräsentiert den nativen Speicher, dh es gibt viele DOM -Knoten in der aktuellen Schnittstelle. Der JavaScript -Speicher bezieht sich auf den JavaScript -Haufen. Wenn Sie diese Schnittstelle als Beispiel nehmen, können Sie feststellen, dass der Wert in den Klammern immer eine stabile Zahl war und sich nicht geändert hat, was bedeutet, dass auf der aktuellen Seite kein Speicherwachstum vorhanden ist. Zu diesem Zeitpunkt können Sie das Klickenereignis erneut auslösen (klicken Sie auf die Schaltfläche), klicken Sie noch ein paar Mal und nach Abschluss werden Sie feststellen, dass der Wert in den Klammern größer geworden ist. Durch diesen Vorgang können Sie den aktuellen Browser -Task -Manager verwenden, um die Änderungen im gesamten Speicher zu überwachen, wenn das Skript ausgeführt wird. Wenn der Wert in den Klammern der aktuellen JavaScript -Spalte weiter steigt, bedeutet dies, dass das Problem ein Problem mit dem Speicher gibt. 4. Timeline -AufzeichnungsinhaltZuvor könnten Sie den integrierten Task-Manager des Browsers verwenden, um Speicheränderungen während der Skriptausführung zu überwachen. Während der Verwendung können Sie jedoch feststellen, dass solche Vorgänge eher verwendet werden, um festzustellen, ob ein Problem mit dem Speicher des aktuellen Skripts vorhanden ist. Wenn Sie herausfinden möchten, mit welchem Skript das Problem in Verbindung steht, ist der Task -Manager nicht so nützlich. Hier führen wir eine Methode zur Aufzeichnung von Speicheränderungen über eine Zeitleiste ein, um zu demonstrieren, wie das Speicherproblem genauer auffindet, mit dem das Speicherproblem in Verbindung steht, oder zu welchem Zeitpunkt es aufgetreten ist. Platzieren Sie zunächst einen DOM -Knoten, fügen Sie ein Klickereignis hinzu, erstellen Sie im Ereignis eine große Anzahl von DOM -Knoten, um den Speicherverbrauch zu simulieren, und verwenden Sie dann ein Array und andere Methoden, um eine sehr lange Zeichenfolge zu bilden, um eine große Menge an Speicherverbrauch zu simulieren. <Text> <button id="btn">hinzufügen</button> <Skript> const obtn = document.getElementById ('btn'); const arrist = []; Funktionstest () { für (sei i = 0; i < 100000; i++) { document.body.appendchild (document.createelement ('p')) } arrlist.push (Neuarray (1000000) .Join ('x')) } obtn.onclick = test; </Skript> </body> Öffnen Sie zunächst das Browser -Konsolen -Tool und wählen Sie standardmäßig das Performance -Panel. Nach dem Klicken startet die Aufzeichnung mehrmals. Nach Abschluss wird ein Diagramm generiert. Wenn der Speicher nicht überprüft wird, werden die Speicheränderungen nicht überwacht. Es enthält viele Informationen und gibt Erklärungen für mehrere Farben. Der blaue ist der JavaScript -Haufen, das rote ist das aktuelle Dokument, der grüne ist der Dom -Knoten, der braune ist der Hörer und der lila eine ist der CPU -Speicher. Für eine leichtere Beobachtung können Sie nur den JavaScript -Haufen aufbewahren und die anderen deaktivieren, um sie zu verbergen. Während der Ausführung dieses Skripts können Sie den Trend des JavaScript -Haufens bisher sehen. Das aktuelle Tool wird als Timing -Diagramm bezeichnet, das sich in der ersten Spalte befindet. Wenn Sie möchten, können Sie die aktuelle Schnittstelle angezeigt. Wenn diese Seite zum ersten Mal geöffnet wird, befindet sie sich tatsächlich lange in einem stabilen Zustand ohne viel Speicherverbrauch. Der Grund dafür ist, dass Add gar nicht geklickt wurde. Zu einem bestimmten Zeitpunkt stieg der Gedächtnis plötzlich auf, und dann war es für eine Weile stabil. Dann stabilisierte es und fällt dann wieder ein. Nach dem Rückgang werden einige kleine Schwankungen vorliegen, die normale Aktivitätskosten sind. Später gab es mehrere aufeinanderfolgende Klicks, die möglicherweise dazu geführt haben, dass die Speicherverwendung stieg, und dann nahm es nach keinem Betrieb wieder ab. Durch einen solchen Speicher -Trend -Diagramm können wir schließen, dass das Speicher im Skript sehr stabil ist. Sobald Sie sehen, dass der Speichertrend in einer geraden Linie steigt, bedeutet dies, dass er nur wächst, aber nicht recycelt wird. Sie können das Problem über das obige Timing -Diagramm finden. Sie können auch die Änderungen an der Schnittstelle sehen, die Ihnen helfen können, zu finden, welcher Teil des Speicherproblems aufgetreten ist. Daher ist es nützlicher als der Task -Manager. 5. Heap Snapshot, um distanzierte DOM zu findenHier ist eine kurze Erklärung, wie die Haufen -Schnappscheide -Funktion zuerst funktioniert. Sobald Sie ein Foto haben, können Sie alle Informationen darin sehen, nämlich der Ursprung der Überwachung. Heap -Schnappschüsse sind bei Verwendung sehr nützlich, da sie eher das Suchverhalten für abgelöste DOM ähneln. Viele Elemente, die auf der Schnittstelle zu sehen sind, sind tatsächlich Dom -Knoten, und diese Dom -Knoten sollten in einem lebenden Dom -Baum vorhanden sein. Es gibt jedoch verschiedene Formen von Dom -Knoten, einer ist ein Müllobjekt und der andere ein abgelöster Dom. Einfach ausgedrückt, wenn dieser Knoten von der DOM -Baum abgelöst wird und im JavaScript -Code kein Dom -Knoten bezeichnet wird, wird er zu Müll. Wenn ein DOM -Knoten nur vom DOM -Baum abgelöst wird, aber im JavaScript -Code immer noch eine Referenz enthält, wird er domiert. Das abgelöste DOM ist in der Schnittstelle unsichtbar, nimmt jedoch den Speicherplatz ein. Diese Situation ist ein Speicherleck, das durch die Heap -Snapshot -Funktion gefunden werden kann. Legen Sie eine BTN -Taste in die HTML und fügen Sie ein Klickereignis hinzu. Erstellen Sie zuerst einen UL -Knoten in der Funktion und erstellen Sie eine Schleife, um mehrere Li -Knoten zu erstellen, und geben Sie sie nach der Erstellung in die Seite. <Text> <button id="btn">hinzufügen</button> <Skript> const obtn = document.getElementById ('btn'); var tmpele; Funktion fn () { var ul = document.createelement ('ul'); für (var i = 0; i <10; i ++) { var li = document.createElement('li'); Ul.Appendchild (li); } tmpele = ul; } obtn.addeventListener ('click', fn); </Skript> </body> Eine einfache Erklärung ist, dass die UL- und Li -Knoten erstellt werden, aber nicht auf der Seite platziert werden. Öffnen Sie das Browser -Debugging -Tool und wählen Sie das Speicherfeld. Nach dem Eintritt finden Sie die Option von Heap Snapshot. Zwei Verhaltenstests werden hier durchgeführt. Ich kehre zur Schnittstelle zurück und mache eine andere Operation. Klicken Sie nach dem Klicken. Dieses Mal werden Sie feststellen, dass die Suche in Snapshot 2 durchgeführt wird. Offensichtlich sind dies die im Code erstellten DOM -Knoten. Dies ist tatsächlich eine Verschwendung von Platz. Funktion fn () { var ul = document.createelement ('ul'); für (var i = 0; i <10; i ++) { var li = document.createElement('li'); Ul.Appendchild (li); } tmpele = ul; // das Dom löschen ul = null; } Unsere kurze Zusammenfassung hier ist, dass wir eine Funktion namens Heap Snapshot verwenden können, die vom Browser bereitgestellt wird, um ein Bild von unserem aktuellen Haufen zu machen. Da sich das getrennte DOM nicht auf der Seite widerspiegelt, ist es in der Erinnerung vorhanden, sodass es zu diesem Zeitpunkt eine Speicherverschwendung ist. 6. Bestimmen Sie, ob es häufig GC gibtHier sprechen wir darüber, wie Sie feststellen können, ob während der Ausführung der aktuellen Webanwendung eine häufige Müllsammlung vorliegt. Die Anwendung wird gestoppt, während der GC zur Arbeit geht. Daher ist häufige GC -Arbeiten nicht freundlich zu Webanwendungen, da sie sich in einem toten Zustand befinden und Benutzer verzögert werden. Zu diesem Zeitpunkt müssen wir einen Weg finden, um festzustellen, ob während der Ausführung der aktuellen Anwendung eine häufige Müllsammlung vorliegt. Hier gibt es zwei Methoden. Wenn Sie feststellen, dass die blaue Trendstange häufig steigt und fällt. Dies bedeutet, dass die Müllsammlung häufig durchgeführt wird. In diesem Fall müssen Sie den entsprechenden Zeitknoten lokalisieren und dann sehen, welche spezifischen Operationen ausgeführt wurden, um dieses Phänomen zu verursachen, und es dann im Code zu handhaben. Der Task -Manager scheint bei Urteilen einfacher zu sein, da es sich nur um eine Änderung eines numerischen Werts handelt. Wenn hier häufige GC -Operationen vorhanden sind, ist die Änderung dieses Wertes eine sofortige Zunahme und sofortige Abnahme. Wenn Sie einen solchen Prozess sehen, gibt es also auch, dass der Code häufig ein Müllabfuhroperationen vorliegt. Die offensichtlichen Auswirkungen häufiger Müllabfuhrvorgänge sind, dass die Benutzer bei der Verwendung sehr langsam sind. ZusammenfassenDies ist das Ende dieses Artikels über den Mechanismus des JavaScript -Mülls. Das könnte Sie auch interessieren:
|
>>: HTML-Grundlagen: HTML-Inhaltsdetails
Dieser Artikel stellt hauptsächlich die Analyse d...
Vorwort Die MySQL-Berechtigungstabelle wird beim ...
Merkmale einer Single-Page-Anwendung „Annahme:“ A...
1. Überprüfen Sie die Vue-Responsive-Nutzung Die...
Szenarioanforderungen 1. Wir können die Skriptfun...
Inhaltsverzeichnis Überblick Code-Implementierung...
Geschäftsszenario: Abfragen von Tabellen in versc...
Inhaltsverzeichnis 1. Einleitung 2. Vererbung der...
Inhaltsverzeichnis 1. React kombiniert mit Antd, ...
Laden Sie zunächst das Installationspaket von der...
Hintergrund Verwenden Sie Idea mit Docker, um den...
Der spezifische Code zum Senden von Emoticons im ...
In diesem Artikel wird der spezifische Code für J...
Installieren Sie JDK: Offizieller Oracle-Download...
Vorwort Wenn wir Webseiten schreiben, stoßen wir ...