Présentation
Les langages de bas niveau, tels que C, ont des primitives de gestion de mémoire de bas niveau, telles que malloc() et free(). Les primitives de mémoire de JavaScript, quant à elles, sont allouées lors de la création de variables (objets, chaînes, etc.) puis libérées « automatiquement » lorsqu'elles ne sont plus utilisées. Ce dernier est appelé garbage collection. Cet "automatique" prête à confusion et donne aux développeurs JavaScript (et autres langages de haut niveau) l'illusion qu'ils n'ont pas à penser à la gestion de la mémoire.
Cycle de vie de la mémoire
Quel que soit le langage de programmation, le cycle de vie de la mémoire est fondamentalement le même :
1. Allouez la mémoire dont vous avez besoin
2. Utilisez-le (lire, écrire)
3. Relâchez-le lorsqu'il n'est pas utilisé ps : Cela a la même signification que "mettre un éléphant au réfrigérateur"
Les première et deuxième parties du processus sont claires dans toutes les langues. La dernière étape est claire dans les langages de bas niveau, mais dans les langages de haut niveau comme JavaScript, la dernière étape n'est pas claire.
Allocation de mémoire JavaScript
Initialisation des variables
Afin de ne pas inquiéter les programmeurs concernant l'allocation, JavaScript complète l'allocation de mémoire lors de la définition des variables.
var o = {
une : 1,
b : nul
}; // Allouer de la mémoire pour l'objet et ses variables contenant
var a = [1, null, "abra"]; // Alloue de la mémoire pour le tableau et ses variables contenant (tout comme les objets)
fonction f(a){
renvoie un 2 ;
} // Allouer de la mémoire pour la fonction (objet appelable)
// Les expressions de fonction peuvent également allouer un objet
someElement.addEventListener('click', function(){
someElement.style.backgroundColor = 'bleu';
}, faux);
Allocation de mémoire via appel de fonction
Certains appels de fonction entraînent une allocation de mémoire d'objet :
Certaines méthodes allouent de nouvelles variables ou de nouveaux objets :
var a = ["ouais ouais", "nan nan"];
var a2 = ["génération", "nan nan"];
var a3 = a.concat(a2); // Il y a quatre éléments dans le nouveau tableau qui connectent le tableau a et le tableau a2.
Utilisation des valeurs
Le processus d'utilisation des valeurs est en fait une opération de lecture et d'écriture de la mémoire allouée, ce qui signifie que vous pouvez écrire une variable ou la valeur d'attribut d'un objet, et même transmettre des paramètres de fonction.
Libérer la mémoire lorsqu'elle n'est plus nécessaire
La plupart des problèmes de gestion de la mémoire surviennent à ce stade. La tâche la plus difficile ici est de constater que la mémoire allouée n’est effectivement plus nécessaire. Cela oblige souvent le développeur à déterminer quelle partie de la mémoire du programme n'est plus nécessaire et à la libérer.
L'interpréteur de langage de haut niveau intègre un "garbage collector" dont la tâche principale est de suivre l'allocation et l'utilisation de la mémoire afin qu'elle puisse être automatiquement libérée lorsque la mémoire allouée n'est plus utilisée. Ce processus est une approximation, car savoir si un certain morceau de mémoire est nécessaire est indécidable (ne peut pas être résolu par un certain algorithme
).Collecte des déchets
Comme mentionné ci-dessus, le problème de la recherche automatique si une certaine mémoire n'est "plus nécessaire" ne peut pas être déterminé. Par conséquent, les implémentations de garbage collection ne peuvent résoudre les problèmes généraux que dans une mesure limitée. Cette section explique les concepts nécessaires pour comprendre les principaux algorithmes de garbage collection et leurs limites.
Citation
Les algorithmes de garbage collection reposent principalement sur la notion de références. Dans le contexte de la gestion de la mémoire, si un objet a l'autorisation d'accéder à un autre objet (implicitement ou explicitement), on l'appelle un objet référençant un autre objet. Par exemple, un objet Javascript possède une référence à son prototype (une référence implicite) et une référence à ses propriétés (une référence explicite).
Ici, le concept d'« objet » n'est pas seulement un objet Javascript spécial, mais inclut également la portée de la fonction (ou portée lexicale globale).
Référence comptage collecte des ordures
Il s'agit de l'algorithme de collecte des ordures le plus simple. Cet algorithme simplifie la définition de « si l'objet n'est plus nécessaire » comme « si l'objet a d'autres objets qui le référencent ». S'il n'y a aucune référence pointant vers l'objet (zéro référence), l'objet sera récupéré par le mécanisme de garbage collection.
Par exemple
var o2 = o; // La variable o2 est la deuxième référence à "cet objet"
o = 1; // Maintenant, la référence originale o de "cet objet" est remplacée par o2
var oa = o2.a; // Référence la propriété a de "cet objet"
// Maintenant, "cet objet" a deux références, l'une est o2 et l'autre est oa
o2 = "yo"; // L'objet d'origine n'a désormais aucune référence
// Il peut être ramassé
// Cependant, l'objet de son attribut a est toujours référencé par oa, il ne peut donc pas encore être recyclé
oa = null; // L'objet avec l'attribut a a désormais également une référence nulle
// Il peut être collecté
Limitation : référence circulaire
Une limitation de cet algorithme simple est que si un objet en référence un autre (formant une référence circulaire), il se peut qu'il ne soit "plus nécessaire", mais il ne sera pas recyclé.
renvoie "azerty";
>
f();
// Deux objets sont créés et se référencent mutuellement, formant une boucle
// Ils ne quitteront pas la portée de la fonction après avoir été appelés
// Ils ne sont donc plus utiles et peuvent être recyclés
// Cependant, l'algorithme de comptage de références prend en compte qu'elles ont toutes au moins une référence les unes aux autres, elles ne seront donc pas recyclées
Exemples pratiques
IE 6, 7 effectue un recyclage par comptage de références sur les objets DOM. Un problème courant pour eux est celui des fuites de mémoire :
Algorithme de marquage et d'effacement
Cet algorithme simplifie la définition de « si l'objet n'est plus nécessaire » comme « si l'objet peut être obtenu ».
Cet algorithme suppose de mettre en place un objet appelé root (en Javascript, root est l'objet global). Périodiquement, le garbage collector démarrera à la racine, trouvera tous les objets référencés depuis la racine, puis trouvera les objets référencés par ces objets... En partant de la racine, le garbage collector trouvera tous les objets pouvant être obtenus et tous les objets qu'on ne peut pas obtenir.
Cet algorithme est meilleur que le précédent, car "objet avec référence nulle" est toujours introuvable, mais l'inverse n'est pas nécessairement vrai, référez-vous à "référence cyclique".
Depuis 2012, tous les navigateurs modernes utilisent l'algorithme de récupération de place Mark-and-Sweep. Toutes les améliorations apportées à l'algorithme de récupération de place JavaScript sont basées sur des améliorations apportées à l'algorithme de balayage de marquage et n'améliorent pas l'algorithme de balayage de marquage lui-même ni sa définition simplifiée de « si un objet n'est plus nécessaire ».
Les références circulaires ne sont plus un problème
Dans l'exemple ci-dessus, après le retour de l'appel de fonction, les deux objets ne peuvent pas être obtenus à partir de l'objet global. Ils seront donc récupérés par le garbage collector.
Dans le deuxième exemple, une fois que le div et ses gestionnaires d'événements sont inaccessibles depuis la racine, ils seront collectés par le garbage collector.
Limitations : les objets doivent être explicitement inaccessibles
Bien qu'il s'agisse d'une limitation, elle est rarement dépassée, c'est pourquoi en réalité peu de gens se soucient du mécanisme de collecte des ordures.