Dieser Artikel bringt Ihnen relevantes Wissen über Javascript, das hauptsächlich Probleme im Zusammenhang mit der Garbage Collection vorstellt. Schauen wir uns das gemeinsam an. Ich hoffe, es wird für alle hilfreich sein.
[Verwandte Empfehlungen: Javascript-Video-Tutorial, Web-Frontend]
Garbage Collection ist der versteckte Mechanismus von JavaScript
. Normalerweise tun wir das nicht Ich muss mich um die Speicherbereinigung kümmern und mich nur auf die Entwicklung von Funktionen konzentrieren. Dies bedeutet jedoch nicht, dass wir uns beim Schreiben von JavaScript
zurücklehnen können. Da die von uns implementierten Funktionen immer komplexer werden und die Menge an Code zunimmt, treten immer schwerwiegendere Leistungsprobleme auf. 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. JavaScript
的隐藏机制,我们通常无需为垃圾回收劳心费力,只需要专注功能的开发就好了。但是这并不意味着我们在编写JavaScript
的时候就可以高枕无忧了,伴随着我们实现的功能越来越复杂,代码量越积越大,性能问题就变的越来越突出。如何写出执行速度更快,而且占用内存更小的代码是程序员永无止歇的追求。一个优秀的程序员总是能在极其有限的资源下,实现惊人的效果,这也正式芸芸众生和高高在上的神祗之间的区别。
代码执行在计算机的内存中,我们在代码中定义的所有变量、对象、函数都会在内存中占用一定的内存空间。在计算机中,内存空间是非常紧张的资源,我们必须时时刻刻注意内存的占用量,毕竟内存条非常贵!如果一个变量、函数或者对象在创建之后不再被后继的代码执行所需要,那么它就可以被称作垃圾。
虽然从直观上理解垃圾的定义非常容易,但是对于一个计算机程序来说,我们很难在某一时刻断定当前存在的变量、函数或者对象在未来不再使用。为了降低计算机内存的开销,同时又保证计算机程序正常执行,我们通常规定满足以下任一条件的对象或者变量为垃圾:
没有被引用的变量或者对象相当于一座没有门的房子,我们永远都无法进入其中,因此不可能在用到它们。无法访问到的对象之间虽然具备连通性,但是仍然无法从外部进入其中,因此也无法再次被利用。满足以上条件的对象或者变量,在程序未来执行过程中绝对不会再次被采用,因此可以放心的当作垃圾回收。
当我们通过以上定义明确了需要丢弃的对象,是否就意味着剩余的变量、对象中就没有垃圾了呢?
不是的!我们当前分辨出的垃圾只是所有垃圾的一部分,仍然会有其他垃圾不满足以上条件,但是也不会再次被使用了。
这是否可以说满足以上定义的垃圾是“绝对垃圾”,其他隐藏在程序中的为“相对垃圾”呢?
垃圾回收机制(GC,Garbage Collection
)负责在程序执行过程中回收无用的变量和内存占用的空间。一个对象虽然没有再次使用的可能,但是仍然存在于内存中的现象被称为内存泄漏。内存泄漏是非常危险的现象,尤其在长时间运行的程序中。如果一个程序出现了内存泄漏,它占用的内存空间就会越来越多,直至耗尽内存。
字符串、对象和数组没有固定的大小,所以只有当它们大小已知时才能对它们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都要分配内存才存储这个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便它们能够被再次利用;否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
JavaScript
的垃圾回收机制会间歇性的检查没有用途的变量、对象(垃圾),并释放条它们占用的空间。
不同的编程语言采用不同的垃圾回收策略,例如C++
就没有垃圾回收机制,所有的内存管理靠程序员本身的技能,这也就造成了C++
比较难以掌握的现状。JavaScript
采用可达性管理内存,从字面意思上看,可达的意思是可以到达,也就是指程序可以通过某种方式访问、使用的变量和对象,这些变量所占用的内存是不可以被释放的。
JavaScript
🎜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. 🎜🎜Können wir sagen, dass Müll, der der obigen Definition entspricht, „absoluter Müll“ ist und anderer im Programm versteckter Müll „relativer Müll“ ist? 🎜🎜3. Garbage Collection🎜🎜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 existiert, obwohl es keine Möglichkeit mehr hat, wieder verwendet zu werden, wird als Memory Leak 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. 🎜🎜Strings, 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 Speicher auf diese Weise 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. 🎜🎜4. Verschiedene Programmiersprachen verwenden unterschiedliche Garbage-Collection-Strategien. Die gesamte Speicherverwaltung hängt beispielsweise von den Fähigkeiten des Programmierers ab hat dazu geführt, dass C++
derzeit relativ schwer zu beherrschen ist. JavaScript
verwendet Erreichbarkeit, um den Speicher zu verwalten. Im wahrsten Sinne des Wortes bedeutet Erreichbarkeit, dass das Programm auf irgendeine Weise auf Variablen und Objekte 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: 🎜genannt, was der oberste Knoten des Erreichbarkeitsbaums ist. Wenn eine Variable oder ein Objekt direkt oder indirekt von der Stammvariablen angewendet wird, gilt die Variable als erreichbar.
Mit anderen Worten: Wenn auf einen Wert über die Wurzel zugegriffen werden kann (z. B. A.b.c.d.e
), dann ist dieser Wert erreichbar.
5. Beispiele für ErreichbarkeitA.b.c.d.e
),那么这个值就是可达的。
let people = { boys:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, girls:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};
以上代码创建了一个对象,并赋值给了变量people
,变量people
中包含了两个对象boys
和girls
,boys
和girls
中又分别包含了两个子对象。这也就创建了一个包含了3
层引用关系的数据结构(不考虑基础类型数据的情况下),如下图:
其中,people
节点由于是全局变量,所以天然可达。boys
和girls
节点由于被全局变量直接引用,构成间接可达。boys1
、boys2
、girls1
和girls2
由于被全局变量间接应用,可以通过people.boys.boys
访问,因此也属于可达变量。
如果我们在以上代码的后面加上以下代码:
people.girls.girls2 = null;people.girls.girls1 = people.boys.boys2;
那么,以上引用层次图将会变成如下形式:
其中,girls1
和girls2
由于和grils
节点断开连接,从而变成了不可达节点,意味着将被垃圾回收机制回收。
而如果此时,我们再执行以下代码:
people.boys.boys2 = null;
那么引用层次图将变成如下形式:
此时,虽然boys
节点和boys2
节点断开了连接,但是由于boys2
节点和girls
节点之间存在引用关系,所以boys2
仍然属于可达的,不会被垃圾回收机制回收。
以上关联关系图证明了为何称全局变量等值为根,因为在关联关系图中,这一类值通常作为关系树的根节点出现。
let people = { boys:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, girls:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};people.boys.boys2.girlfriend = people.girls.girls1; //boys2引用girls1people.girls.girls1.boyfriend = people.boys.boys2; //girls1引用boys2
以上代码在boys2
和girls1
之间创建了一个相互关联的关系,关系结构图如下:
此时,如果我们切断boys
和boys2
之间的关联:
delete people.boys.boys2;
对象之间的关联关系图如下:
显然,并没有不可达的节点出现。
此时,如果我们切断boyfriend
关系连接:
delete people.girls.girls1;
关系图变为:
此时,虽然boys2
和girls1
之间还存在girlfriend
关系,但是,boys2
let people = { boys:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, girls:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};delete people.boys;delete people.girls;
people
zu, die zwei Objekte boys
enthält und girls
werden erstellt, und boys
und girls
enthalten jeweils zwei Unterobjekte. Dadurch wird auch eine Datenstruktur erstellt, die die Referenzbeziehung der 3
-Ebene enthält (ohne Berücksichtigung der grundlegenden Typdaten), wie unten gezeigt:
Unter diesen ist der Knoten people
natürlich erreichbar, da es sich um eine globale Variable handelt. . Die Knoten boys
und girls
sind indirekt erreichbar, da sie direkt von globalen Variablen referenziert werden. boys1
, boys2
, girls1
und girls2
werden indirekt von globalen Variablen verwendet und können durch people weitergegeben werden .boys .boys
Zugriff, daher ist es auch eine erreichbare Variable.
Wenn wir nach dem obigen Code den folgenden Code hinzufügen: let user = {username:'xiaoming'};
//对象被user变量引用,计数+1
let user2 = user;
//对象被新的变量引用,计数+1
user = null;
//变量不再引用对象,计数-1
user2 = null;
//变量不再引用对象,奇数-1
//此时,对象引用数为0,会被删除
girls1
und girls2
auf grils zurückzuführen -Knoten wird getrennt und wird zu einem nicht erreichbaren Knoten, was bedeutet, dass er vom Garbage-Collection-Mechanismus recycelt wird. <strong></strong>Und wenn wir zu diesem Zeitpunkt den folgenden Code ausführen:
let boy = {}; let girl = {}; boy.girlfriend = girl; girl.boyfriend = boy; boy = null; girl = null;
Dann wird das Referenzhierarchiediagramm die folgende Form annehmen:
Zu diesem Zeitpunkt, obwohl der Knoten boys
und der Knoten boys2
vorhanden sind getrennt. Aufgrund der Referenzbeziehung zwischen dem Knoten boys2
und dem Knoten girls
ist boys2
jedoch immer noch erreichbar und wird vom Knoten nicht wiederverwendet Garbage-Collection-Mechanismus.
🎜Zusammenhängend: 🎜rrreee🎜Der obige Code erstellt eine Wechselbeziehung zwischenDas obige Assoziationsdiagramm beweist, warum der äquivalente Wert globaler Variablen
Wurzelgenannt wird, da dieser Werttyp im Assoziationsdiagramm normalerweise als Wurzelknoten des Beziehungsbaums erscheint.
boys2
und girls1
. Das Beziehungsstrukturdiagramm lautet wie folgt: 🎜🎜🎜🎜An dieser Stelle, wenn wir abschneiden Die Assoziation zwischen Jungen
und boys2
: 🎜rrreee🎜Das Assoziationsdiagramm zwischen Objekten ist wie folgt: 🎜🎜🎜🎜Offensichtlich gibt es keine nicht erreichbaren Knoten. 🎜🎜Wenn wir an diesem Punkt die Beziehungsverbindung Freund
unterbrechen: 🎜rrreee🎜Das Beziehungsdiagramm wird zu: 🎜🎜🎜🎜Zu diesem Zeitpunkt gibt es zwar noch eine Freundin zwischen <code>boys2
und Die Beziehung girls1
hingegen wird zu einem nicht erreichbaren Knoten und wird vom Garbage-Collection-Mechanismus zurückgefordert. 🎜🎜🎜Zugängliche Insel: 🎜🎜rrreee🎜Das durch den obigen Code gebildete Referenzhierarchiediagramm lautet wie folgt: 🎜🎜🎜🎜🎜Obwohl zu diesem Zeitpunkt noch gegenseitige Referenzbeziehungen zwischen den Objekten innerhalb des gepunkteten Felds bestehen, sind dies bei diesen Objekten der Fall ebenfalls unzugänglich und wird durch den Garbage-Collection-Mechanismus gelöscht. Diese Knoten haben ihre Beziehung zur 🎜Wurzel🎜 verloren und sind nicht mehr erreichbar. 🎜🎜6. Garbage-Collection-Algorithmus🎜🎜🎜Referenzzählung🎜🎜🎜Der sogenannte Referenzzähler zählt, wie der Name schon sagt, jedes Mal, wenn ein Verweis hinzugefügt wird, und wenn ein Verweis hinzugefügt wird Wenn der Referenzzähler gelöscht wird, wird eins abgezogen. Wenn er 0 ist, wird er als Müll betrachtet und das Objekt wird gelöscht, um den Speicher zurückzugewinnen. 🎜🎜Zum Beispiel: 🎜rrreee🎜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 = girl; girl.boyfriend = boy; boy = null; girl = null;
以上代码在boy
和girl
之间存在相互引用,计数删掉boy
和girl
内的引用,二者对象并不会被回收。由于循环引用的存在,两个匿名对象的引用计数永远不会归零,也就产生了内存泄漏。
在C++
中存在一个智能指针(shared_ptr
)的概念,程序员可以通过智能指针,利用对象析构函数释放引用计数。但是对于循环引用的状况就会产生内存泄漏。
好在JavaScript
已经采用了另外一种更为安全的策略,更大程度上避免了内存泄漏的风险。
标记清除(mark and sweep
)是JavaScript
引擎采取的垃圾回收算法,其基本原理是从根出发,广度优先遍历变量之间的引用关系,对于遍历过的变量打上一个标记(优秀员工徽章
),最后删除没有标记的对象。
算法基本过程如下:
2
步,直至无新的优秀员工加入;举个栗子:
如果我们程序中存在如下图所示的对象引用关系:
我们可以清晰的看到,在整个图片的右侧存在一个“可达孤岛”,从根出发,永远无法到达孤岛。但是垃圾回收器并没有我们这种上帝视角,它们只会根据算法会首先把根节点打上优秀员工标记。
然后从优秀员工出发,找到所有被优秀员工引用的节点,如上图中虚线框中的三个节点。然后把新找到的节点同样打上优秀员工标记。
反复执行查找和标记的过程,直至所有能找到的节点都被成功标记。
最终达到下图所示的效果:
由于在算法执行周期结束之后,右侧的孤岛仍然没有标记,因此会被垃圾回收器任务无法到达这些节点,最终被清除。
如果学过数据结构和算法的童鞋可能会惊奇的发现,这不就是图的遍历吗,类似于连通图算法。
垃圾回收是一个规模庞大的工作,尤其在代码量非常大的时候,频繁执行垃圾回收算法会明显拖累程序的执行。JavaScript
算法在垃圾回收上做了很多优化,从而在保证回收工作正常执行的前提下,保证程序能够高效的执行。
性能优化采取的策略通常包括以下几点:
JavaScript
程序在执行过程中会维持相当量级的变量数目,频繁扫描这些变量会造成明显的开销。但是这些变量在生命周期上各有特点,例如局部变量会频繁的创建,迅速的使用,然后丢弃,而全局变量则会长久的占据内存。JavaScript
把两类对象分开管理,对于快速创建、使用并丢弃的局部变量,垃圾回收器会频繁的扫描,保证这些变量在失去作用后迅速被清理。而对于哪些长久把持内存的变量,降低检查它们的频率,从而节约一定的开销。
增量式的思想在性能优化上非常常见,同样可以用于垃圾回收。在变量数目非常大时,一次性遍历所有变量并颁发优秀员工标记显然非常耗时,导致程序在执行过程中存在卡顿。所以,引擎会把垃圾回收工作分成多个子任务,并在程序执行的过程中逐步执行每个小任务,这样就会造成一定的回收延迟,但通常不会造成明显的程序卡顿。
CPU
funktioniert nicht immer, selbst in komplexen Programmen. Dies liegt hauptsächlich daran, dass die CPU
sehr schnell arbeitet und der periphere IO
ist oft um mehrere Größenordnungen langsamer, daher ist die Einrichtung einer Speicherbereinigungsstrategie im Leerlauf der CPU
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. CPU
即使是在复杂的程序中也不是一直都有工作的,这主要是因为CPU
工作的速度非常快,外围IO
往往慢上几个数量级,所以在CPU
空闲的时候安排垃圾回收策略是一种非常有效的性能优化手段,而且基本不会对程序本身造成不良影响。这种策略就类似于系统的空闲时间升级一样,用户根本察觉不到后台的执行。
本文的主要任务是简单的结束垃圾回收的机制、常用的策略和优化的手段,并不是为了让大家深入了解引擎的后台执行原理。
通过本文,你应该了解:
JavaScript
JavaScript
. Sie wird im Hintergrund ausgeführt und wir müssen uns darüber keine Sorgen machen Markierungsstrategien können eindeutig Speicherlecks vermeiden, die durch erreichbare Inseln verursacht werden -Ende]
Das obige ist der detaillierte Inhalt vonZusammenfassung der Garbage-Collection-Kenntnisse über den versteckten JavaScript-Mechanismus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!