Maison > Article > interface Web > Apprenez le mécanisme de récupération de place de JavaScript et la gestion de la mémoire grâce aux compétences me_javascript
1. Mécanisme de collecte des déchets—GC
Javascript dispose d'un mécanisme de garbage collection automatique (GC : Garbage Collection), ce qui signifie que l'environnement d'exécution est responsable de la gestion de la mémoire utilisée lors de l'exécution du code.
Principe : Le garbage collector va périodiquement (périodiquement) découvrir les variables qui ne sont plus utilisées, puis libérer leur mémoire.
Le mécanisme du garbage collection JavaScript est très simple : rechercher les variables qui ne sont plus utilisées, puis libérer la mémoire qu'elles occupent. Cependant, ce processus n'est pas en temps réel car sa surcharge est relativement importante, donc le garbage collector. suivra le calendrier fixe Exécuté périodiquement à un certain intervalle de temps .
Les variables qui ne sont plus utilisées sont des variables dont le cycle de vie est terminé. Bien entendu, il ne peut s'agir que de variables locales. Le cycle de vie des variables globales ne se terminera que lorsque le navigateur déchargera la page. Les variables locales n'existent que pendant l'exécution de la fonction, et pendant ce processus, l'espace correspondant sera alloué aux variables locales sur la pile ou le tas pour stocker leurs valeurs, puis ces variables seront utilisées dans la fonction jusqu'à la fin de la fonction. , et la fermeture En raison des fonctions internes du package, les fonctions externes ne peuvent pas être considérées comme la fin.
Expliquons le code :
function fn1() { var obj = {name: 'hanzichi', age: 10}; } function fn2() { var obj = {name:'hanzichi', age: 10}; return obj; } var a = fn1(); var b = fn2();
Voyons comment le code est exécuté. Tout d'abord, deux fonctions sont définies, appelées respectivement fn1 et fn2 Lorsque fn1 est appelé, entrant dans l'environnement de fn1, une mémoire sera ouverte pour stocker l'objet {nom : 'hanzichi', âge : 10}, et lorsque l'appel est effectué. terminé, l'environnement fn1, alors ce bloc de mémoire sera automatiquement libéré par le garbage collector dans le moteur js pendant le processus d'appel de fn2, l'objet retourné est pointé par la variable globale b, donc ce bloc de mémoire sera ne soit pas libéré.
La question se pose ici : Quelle variable est inutile ? Par conséquent, le garbage collector doit garder une trace des variables qui sont inutiles et marquer celles qui ne sont plus utiles pour se préparer à récupérer leur mémoire à l'avenir. La stratégie utilisée pour marquer les variables inutilisées peut varier d'une implémentation à l'autre, mais il existe généralement deux implémentations : le balayage de marquage et le comptage de références. Le comptage de références est moins couramment utilisé, le marquage et le balayage sont plus couramment utilisés.
2. Effacement des marques
La méthode de récupération de place la plus couramment utilisée dans js est l'effacement des marques. Lorsqu'une variable entre dans l'environnement, par exemple en déclarant une variable dans une fonction, la variable est marquée comme « entrée dans l'environnement ». Logiquement, la mémoire occupée par les variables entrant dans l'environnement ne pourra jamais être libérée, puisqu'elles pourront être utilisées à chaque fois que le flux d'exécution entre dans l'environnement correspondant. Et lorsqu'une variable quitte l'environnement, elle est marquée comme "quittant l'environnement".
function test(){ var a = 10 ; //被标记 ,进入环境 var b = 20 ; //被标记 ,进入环境 } test(); //执行完毕 之后 a、b又被标离开环境,被回收。
Lorsque le garbage collector s'exécutera, il marquera toutes les variables stockées en mémoire (bien sûr, n'importe quelle méthode de marquage peut être utilisée). Ensuite, il supprime les balises (fermetures) des variables de l'environnement et des variables référencées par les variables de l'environnement. Les variables marquées après cela seront considérées comme des variables à supprimer car les variables de l'environnement ne peuvent plus accéder à ces variables. Enfin, le garbage collector termine le travail de nettoyage de la mémoire, détruisant les valeurs marquées et récupérant l'espace mémoire qu'elles occupent.
Jusqu'à présent, les implémentations js d'IE, Firefox, Opera, Chrome et Safari utilisent toutes des stratégies de récupération de place par marquage et balayage ou des stratégies similaires, mais les intervalles de temps de récupération de place sont différents.
3. Nombre de références
La signification du comptage de références est de suivre le nombre de fois où chaque valeur est référencée. Lorsqu'une variable est déclarée et qu'une valeur de type référence est affectée à la variable, le nombre de références à cette valeur est 1. Si la même valeur est affectée à une autre variable, le nombre de références de la valeur est augmenté de 1. A l'inverse, si la variable contenant une référence à cette valeur obtient une autre valeur, le nombre de références à cette valeur est décrémenté de un. Lorsque le nombre de références à cette valeur devient 0, cela signifie qu'il n'y a plus moyen d'accéder à cette valeur, donc l'espace mémoire qu'elle occupe peut être récupéré. De cette façon, lors de la prochaine exécution du garbage collector, il libérera la mémoire occupée par ces valeurs avec un nombre de références de 0.
function test(){ var a = {} ; //a的引用次数为0 var b = a ; //a的引用次数加1,为1 var c =a; //a的引用次数再加1,为2 var b ={}; //a的引用次数减1,为1 }
Netscape Navigator3 a été le premier navigateur à utiliser la stratégie de comptage de références, mais il a rapidement rencontré un sérieux problème : les références circulaires. Une référence circulaire signifie que l'objet A contient un pointeur vers l'objet B et que l'objet B contient également une référence vers l'objet A.
function fn() { var a = {}; var b = {}; a.pro = b; b.pro = a; } fn();
以上代码a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。
我们知道,IE中有一部分对象并不是原生js对象。例如,其内存泄露DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.e = element; element.o = myObject;
这个例子在一个DOM元素(element)与一个原生js对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象;而变量element也有一个属性名为o回指myObject。由于存在这个循环引用,即使例子中的DOM从页面中移除,它也永远不会被回收。
看上面的例子,有同学回觉得太弱了,谁会做这样无聊的事情,其实我们是不是就在做
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; };
这段代码看起来没什么问题,但是obj引用了document.getElementById(“element”),而document.getElementById(“element”)的onclick方法会引用外部环境中德变量,自然也包括obj,是不是很隐蔽啊。
解决办法
最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样
myObject.element = null; element.o = null;
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; obj=null; };
Définir une variable sur null signifie rompre le lien entre la variable et la valeur à laquelle elle faisait précédemment référence. Lors de la prochaine exécution du garbage collector, ces valeurs sont supprimées et la mémoire qu'elles occupent est récupérée.
Il est à noter qu'IE9 n'a pas de problème de fuite de mémoire Dom causée par des références circulaires. Il se peut que Microsoft ait fait des optimisations ou que la façon dont Dom est recyclé ait changé
4. Gestion de la mémoire
1. Quand la collecte des déchets est-elle déclenchée ?
Le garbage collector s'exécute périodiquement. Si beaucoup de mémoire est allouée, le travail de recyclage sera très ardu. Déterminer l'intervalle de garbage collection devient une question qui mérite réflexion. Le garbage collection d'IE6 s'exécute en fonction de la quantité d'allocation de mémoire. Lorsqu'il y a 256 variables, 4 096 objets ou 64 000 chaînes dans l'environnement, le garbage collector sera déclenché, cela semble très scientifique et il n'est pas nécessaire d'appuyer sur un paragraphe. On ne l'appelle qu'une fois à chaque fois, parfois ce n'est pas nécessaire, ne serait-il pas bien de l'appeler à la demande ? Mais s'il y a autant de variables dans l'environnement et que le script est si complexe maintenant, c'est normal, alors le résultat est que le ramasse-miettes fonctionne toujours, donc le navigateur ne peut pas jouer.
Microsoft a effectué des ajustements dans IE7. Les conditions de déclenchement ne sont plus fixes, mais modifiées dynamiquement. La valeur initiale est la même que dans IE6 si la quantité d'allocation mémoire récupérée par le garbage collector est inférieure à 15 % de la mémoire occupée. par le programme, cela signifie que la majeure partie de la mémoire ne peut pas être recyclée. La condition de déclenchement définie pour le garbage collection est trop sensible à ce moment-là, doublez la condition de la rue. Si la mémoire recyclée est supérieure à 85 %, cela signifie que la majeure partie de la mémoire est recyclée. la mémoire devrait être nettoyée il y a longtemps. À ce moment-là, rétablissez la condition de déclenchement. Cela rend la fonction de collecte des ordures beaucoup plus facile
2. Plan GC raisonnable
1) La solution GC de base du moteur Javascript est (GC simple) : marquer et balayer, c'est-à-dire :
2), défauts GC
Comme d'autres langages, la stratégie GC de JavaScript ne peut éviter un problème : lors du GC, elle cesse de répondre aux autres opérations. C'est pour des raisons de sécurité. Le GC de Javascript est de 100 ms ou plus, ce qui est bien pour les applications générales, mais pour les jeux JS et les applications d'animation qui nécessitent une continuité relativement élevée, c'est gênant. C’est ce que le nouveau moteur doit optimiser : éviter les longues pauses de réponse provoquées par le GC.
3), Stratégie d'optimisation du GC
Oncle David a principalement introduit 2 plans d'optimisation, et ce sont aussi les deux plans d'optimisation les plus importants :
(1) Génération GC (Génération GC)
Cela est cohérent avec l'idée de la stratégie de recyclage Java. Le but est de faire la distinction entre les objets « temporaires » et « persistants » ; recycler davantage de zones « d'objets temporaires » (jeune génération) et moins de zones « d'objets persistants » (génération titulaire) pour réduire le nombre d'objets à parcourir à chaque fois. , réduisant ainsi la consommation de temps de chaque GC. Comme le montre l'image :
Ce qu'il faut ajouter ici, c'est : pour les objets de génération titulaire, il y a une surcharge supplémentaire : le migrer de la jeune génération vers la génération titulaire. De plus, s'il est référencé, le point de référence doit également être modifié.
(2) GC incrémentiel
L'idée decette solution est très simple, c'est-à-dire "traiter un peu à chaque fois, puis traiter un peu la prochaine fois, et ainsi de suite". Comme le montre l'image :
Bien que cette solution prenne peu de temps, elle entraîne de nombreuses interruptions et pose le problème des changements de contexte fréquents.
Étant donné que chaque solution a ses scénarios et ses lacunes applicables, dans les applications réelles, la solution sera sélectionnée en fonction de la situation réelle.
Par exemple : lorsque le rapport (objet/s) est faible, la fréquence d'exécution des interruptions GC est plus faible, et le GC simple est plus faible si un grand nombre d'objets "survivent" pendant une longue période, l'avantage du générationnel ; le traitement n'est pas génial.
Référence :
Ce qui précède concerne le mécanisme de récupération de place et la gestion de la mémoire de JavaScript. J'espère que cela sera utile à l'apprentissage de chacun.