Maison > Article > interface Web > Résumé des connaissances sur le garbage collection du mécanisme caché JavaScript
Cet article vous apporte des connaissances pertinentes sur javascript, qui présente principalement les problèmes liés au garbage collection. Le garbage collection est le mécanisme caché de JavaScript. Examinons-le ensemble, j'espère que cela sera utile à tout le monde.
[Recommandations associées : tutoriel vidéo javascript, front-end web]
Le garbage collection est le mécanisme caché de JavaScript
. vous devez vous soucier du garbage collection, concentrez-vous simplement sur le développement des fonctions. Mais cela ne signifie pas que nous pouvons nous asseoir et nous détendre lors de l'écriture de JavaScript
. À mesure que les fonctions que nous implémentons deviennent de plus en plus complexes et que la quantité de code s'accumule, les problèmes de performances deviennent de plus en plus graves. Comment écrire du code qui s'exécute plus rapidement et occupe moins de mémoire est la quête sans fin des programmeurs. Un excellent programmeur peut toujours obtenir des résultats étonnants avec des ressources extrêmement limitées. C'est aussi la différence entre les êtres ordinaires et les dieux distants. JavaScript
的隐藏机制,我们通常无需为垃圾回收劳心费力,只需要专注功能的开发就好了。但是这并不意味着我们在编写JavaScript
的时候就可以高枕无忧了,伴随着我们实现的功能越来越复杂,代码量越积越大,性能问题就变的越来越突出。如何写出执行速度更快,而且占用内存更小的代码是程序员永无止歇的追求。一个优秀的程序员总是能在极其有限的资源下,实现惊人的效果,这也正式芸芸众生和高高在上的神祗之间的区别。
代码执行在计算机的内存中,我们在代码中定义的所有变量、对象、函数都会在内存中占用一定的内存空间。在计算机中,内存空间是非常紧张的资源,我们必须时时刻刻注意内存的占用量,毕竟内存条非常贵!如果一个变量、函数或者对象在创建之后不再被后继的代码执行所需要,那么它就可以被称作垃圾。
虽然从直观上理解垃圾的定义非常容易,但是对于一个计算机程序来说,我们很难在某一时刻断定当前存在的变量、函数或者对象在未来不再使用。为了降低计算机内存的开销,同时又保证计算机程序正常执行,我们通常规定满足以下任一条件的对象或者变量为垃圾:
没有被引用的变量或者对象相当于一座没有门的房子,我们永远都无法进入其中,因此不可能在用到它们。无法访问到的对象之间虽然具备连通性,但是仍然无法从外部进入其中,因此也无法再次被利用。满足以上条件的对象或者变量,在程序未来执行过程中绝对不会再次被采用,因此可以放心的当作垃圾回收。
当我们通过以上定义明确了需要丢弃的对象,是否就意味着剩余的变量、对象中就没有垃圾了呢?
不是的!我们当前分辨出的垃圾只是所有垃圾的一部分,仍然会有其他垃圾不满足以上条件,但是也不会再次被使用了。
这是否可以说满足以上定义的垃圾是“绝对垃圾”,其他隐藏在程序中的为“相对垃圾”呢?
垃圾回收机制(GC,Garbage Collection
)负责在程序执行过程中回收无用的变量和内存占用的空间。一个对象虽然没有再次使用的可能,但是仍然存在于内存中的现象被称为内存泄漏。内存泄漏是非常危险的现象,尤其在长时间运行的程序中。如果一个程序出现了内存泄漏,它占用的内存空间就会越来越多,直至耗尽内存。
字符串、对象和数组没有固定的大小,所以只有当它们大小已知时才能对它们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都要分配内存才存储这个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便它们能够被再次利用;否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
JavaScript
的垃圾回收机制会间歇性的检查没有用途的变量、对象(垃圾),并释放条它们占用的空间。
不同的编程语言采用不同的垃圾回收策略,例如C++
就没有垃圾回收机制,所有的内存管理靠程序员本身的技能,这也就造成了C++
比较难以掌握的现状。JavaScript
采用可达性管理内存,从字面意思上看,可达的意思是可以到达,也就是指程序可以通过某种方式访问、使用的变量和对象,这些变量所占用的内存是不可以被释放的。
JavaScript
🎜Lorsque nous clarifions les objets qui doivent être supprimés grâce à la définition ci-dessus, cela signifie-t-il qu'il n'y a pas de déchets dans les variables et objets restants ? 🎜🎜Non ! Les déchets que nous identifions actuellement ne représentent qu'une partie de tous les déchets. Il y aura encore d'autres déchets qui ne rempliront pas les conditions ci-dessus, mais ils ne seront plus utilisés. 🎜🎜Pouvons-nous dire que les déchets qui répondent à la définition ci-dessus sont des « déchets absolus » et que les autres déchets cachés dans le programme sont des « déchets relatifs » ? 🎜🎜3. Garbage Collection🎜🎜Le mécanisme de garbage collection (
GC, Garbage Collection
) est responsable du recyclage des variables inutiles et de l'espace mémoire occupé lors de l'exécution du programme. Le phénomène selon lequel un objet existe toujours en mémoire même s'il n'a aucune possibilité d'être réutilisé est appelé une fuite de mémoire. Les fuites de mémoire sont un phénomène très dangereux, en particulier dans les programmes de longue durée. Si un programme présente une fuite de mémoire, il occupera de plus en plus d’espace mémoire jusqu’à manquer de mémoire. 🎜🎜Les chaînes, les objets et les tableaux n'ont pas de tailles fixes, donc leur allocation dynamique de stockage ne peut être effectuée que lorsque leurs tailles sont connues. Chaque fois qu'un programme JavaScript crée une chaîne, un tableau ou un objet, l'interpréteur alloue de la mémoire pour stocker l'entité. Chaque fois que la mémoire est allouée dynamiquement de cette manière, elle doit éventuellement être libérée pour pouvoir être réutilisée ; sinon, l'interpréteur JavaScript consommera toute la mémoire disponible dans le système, provoquant un crash du système. Le mécanisme de récupération de place de 🎜🎜JavaScript
vérifiera par intermittence les variables et les objets inutiles (garbage) et libérera l'espace qu'ils occupent. 🎜🎜4. Accessibilité🎜🎜Différents langages de programmation adoptent différentes stratégies de garbage collection. Par exemple, C++
n'a pas de mécanisme de garbage collection. Toute la gestion de la mémoire repose sur les compétences du programmeur. a abouti à la situation actuelle dans laquelle le C++
est relativement difficile à maîtriser. JavaScript
utilise l'accessibilité pour gérer la mémoire. Littéralement, l'accessibilité signifie qu'elle peut être atteinte, ce qui signifie que le programme peut accéder et utiliser les variables et les objets d'une manière ou d'une autre. la mémoire occupée par ces variables ne peut pas être libérée. 🎜🎜JavaScript
spécifie un ensemble inhérent de valeurs accessibles, et les valeurs de l'ensemble sont intrinsèquement accessibles : 🎜Si une variable ou un objet est directement ou indirectement appliqué par la variable racine, la variable est considérée comme accessible. En d'autres termes, si une valeur est accessible via la racine (par exemple, A.b.c.d.e
), alors cette valeur est accessible.
5. Exemples d'accessibilité
A.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
Association hiérarchique :
let people = { boys:{ boys1:{name:'xiaoming'}, boys2:{name:'xiaojun'}, }, girls:{ girls1:{name:'xiaohong'}, girls2:{name:'huahua'}, }};delete people.boys;delete people.girls;Le code ci-dessus crée un objet et l'assigne à la variable
personnes
, qui contient deux objets garçons
. et girls
sont créés, et boys
et girls
contiennent respectivement deux sous-objets. Cela crée également une structure de données contenant la relation de référence de couche 3
(sans tenir compte des données de type de base), comme indiqué ci-dessous :
people
est naturellement accessible car il s'agit d'une variable globale. . Les nœuds garçons
et filles
sont indirectement accessibles car ils sont directement référencés par des variables globales. boys1
, boys2
, girls1
et girls2
sont indirectement utilisés par les variables globales et peuvent être transmis via people .boys .boys
Access, c'est donc aussi une variable accessible. Si nous ajoutons le code suivant après le code ci-dessus :
let user = {username:'xiaoming'}; //对象被user变量引用,计数+1 let user2 = user; //对象被新的变量引用,计数+1 user = null; //变量不再引用对象,计数-1 user2 = null; //变量不再引用对象,奇数-1 //此时,对象引用数为0,会被删除
Alors le diagramme hiérarchique de référence ci-dessus deviendra le suivant :
girls1
et girls2
sont dues à grils est déconnecté et devient un nœud inaccessible, ce qui signifie qu'il sera recyclé par le mécanisme de garbage collection.
let boy = {}; let girl = {}; boy.girlfriend = girl; girl.boyfriend = boy; boy = null; girl = null;Alors le diagramme hiérarchique de référence deviendra la forme suivante :
À l'heure actuelle, bien que le nœud boys
et le nœud boys2
soient déconnecté, cependant, en raison de la relation de référence entre le nœud boys2
et le nœud girls
, boys2
est toujours accessible et ne sera pas recyclé par le mécanisme de collecte des déchets.
Le diagramme d'association ci-dessus prouve pourquoi la valeur équivalente des variables globales est appelée
root, car dans le diagramme d'association, ce type de valeur apparaît généralement comme le nœud racine de l'arbre des relations.
Interdépendant :
rrreee🎜Le code ci-dessus crée une relation interdépendante entre lesboys2
et les girls1
. Le diagramme de structure de la relation est le suivant : 🎜🎜🎜🎜À ce stade, si nous coupons L'association entre les garçons
et les boys2
: 🎜rrreee🎜Le schéma d'association entre les objets est le suivant : 🎜🎜🎜🎜Évidemment, il n'y a aucun nœud inaccessible. 🎜🎜À ce moment-là, si nous coupons la connexion relationnelle petit-ami
: 🎜rrreee🎜Le diagramme relationnel devient : 🎜🎜🎜🎜En ce moment, bien qu'il y ait encore une petite amie entre <code>boys2
et girls1
, cependant, boys2
et devient un nœud inaccessible et sera récupéré par le mécanisme de récupération de place. 🎜🎜🎜Île accessible : 🎜🎜rrreee🎜Le diagramme hiérarchique de référence formé par le code ci-dessus est le suivant : 🎜🎜🎜🎜🎜À l'heure actuelle, bien qu'il existe encore des relations de référence mutuelles entre les objets à l'intérieur de la boîte en pointillés, ces objets sont également inaccessible et sera supprimé par le mécanisme de garbage collection. Ces nœuds ont perdu leur relation avec la 🎜root🎜 et deviennent inaccessibles. 🎜🎜6. Algorithme de collecte des déchets🎜🎜🎜Comptage de références🎜🎜🎜Le soi-disant nombre de références, comme son nom l'indique, compte chaque fois qu'un objet est référencé, il sera augmenté de un, et lorsqu'une référence est supprimée, elle sera diminuée de un. Si le numéro de référence change. S'il est égal à 0, il est considéré comme un déchet et l'objet est supprimé pour récupérer la mémoire. 🎜🎜Par exemple : 🎜rrreee🎜Bien que la méthode de comptage de références semble très raisonnable, en fait, il existe des failles évidentes dans le mécanisme de recyclage de la mémoire utilisant la méthode de comptage de références. 🎜🎜Par exemple : 🎜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
把两类对象分开管理,对于快速创建、使用并丢弃的局部变量,垃圾回收器会频繁的扫描,保证这些变量在失去作用后迅速被清理。而对于哪些长久把持内存的变量,降低检查它们的频率,从而节约一定的开销。
增量式的思想在性能优化上非常常见,同样可以用于垃圾回收。在变量数目非常大时,一次性遍历所有变量并颁发优秀员工标记显然非常耗时,导致程序在执行过程中存在卡顿。所以,引擎会把垃圾回收工作分成多个子任务,并在程序执行的过程中逐步执行每个小任务,这样就会造成一定的回收延迟,但通常不会造成明显的程序卡顿。
Le CPU
ne fonctionne pas toujours, même dans les programmes complexes, principalement parce que le CPU
fonctionne très rapidement et le IO
périphérique. > est souvent plusieurs ordres de grandeur plus lent, donc organiser une stratégie de récupération de place lorsque le CPU
est inactif est une méthode d'optimisation des performances très efficace, et elle n'entraînera fondamentalement aucun effet négatif sur le programme lui-même. Cette stratégie est similaire à la mise à niveau du temps d'inactivité du système, et les utilisateurs ne sont pas du tout conscients de l'exécution en arrière-plan. CPU
即使是在复杂的程序中也不是一直都有工作的,这主要是因为CPU
工作的速度非常快,外围IO
往往慢上几个数量级,所以在CPU
空闲的时候安排垃圾回收策略是一种非常有效的性能优化手段,而且基本不会对程序本身造成不良影响。这种策略就类似于系统的空闲时间升级一样,用户根本察觉不到后台的执行。
本文的主要任务是简单的结束垃圾回收的机制、常用的策略和优化的手段,并不是为了让大家深入了解引擎的后台执行原理。
通过本文,你应该了解:
JavaScript
JavaScript
. Il est exécuté en arrière-plan et nous n'avons pas à nous en soucier ; les stratégies de marquage peuvent clairement éviter les fuites de mémoire causées par les îles accessibles[Recommandations associées :
tutoriel vidéo javascript🎜, 🎜front web. -fin🎜]🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!