Qu'est-ce que la récupération de place JavaScript?

Réponses:

192

Eric Lippert a écrit un article de blog détaillé sur ce sujet il y a quelque temps (en le comparant également à VBScript ). Plus précisément, il a écrit sur JScript , qui est la propre implémentation d'ECMAScript par Microsoft, bien que très similaire à JavaScript. J'imagine que vous pouvez supposer que la grande majorité du comportement serait la même pour le moteur JavaScript d'Internet Explorer. Bien sûr, l'implémentation variera d'un navigateur à l'autre, bien que je soupçonne que vous pourriez prendre un certain nombre de principes communs et les appliquer à d'autres navigateurs.

Cité de cette page:

JScript utilise un ramasse-miettes non générationnel à balayage et à balayage. Cela fonctionne comme ceci:

  • Chaque variable qui est "dans la portée" est appelée un "récupérateur". Un charognard peut faire référence à un nombre, un objet, une chaîne, peu importe. Nous maintenons une liste de récupérateurs - les variables sont déplacées vers la liste scav lorsqu'elles entrent dans la portée et hors de la liste scav lorsqu'elles sortent de la portée.

  • De temps en temps, le garbage collector s'exécute. Tout d'abord, il met une "marque" sur chaque objet, variable, chaîne, etc. - toute la mémoire suivie par le GC. (JScript utilise la structure de données VARIANT en interne et il y a beaucoup de bits supplémentaires inutilisés dans cette structure, nous en définissons donc juste un.)

  • Deuxièmement, il efface la marque sur les charognards et la fermeture transitive des références de charognards. Donc, si un objet récupérateur fait référence à un objet non embarqué, nous effaçons les bits sur le non récupérateur et sur tout ce à quoi il se réfère. (J'utilise le mot «fermeture» dans un sens différent de celui de mon post précédent.)

  • À ce stade, nous savons que toute la mémoire encore marquée est une mémoire allouée qui ne peut être atteinte par aucun chemin à partir d'une variable dans la portée. Tous ces objets sont invités à se détruire, ce qui détruit toutes les références circulaires.

Le but principal de la récupération de place est de permettre au programmeur de ne pas se soucier de la gestion de la mémoire des objets qu'il crée et utilise, bien que, bien sûr, il ne soit pas possible de l'éviter parfois - il est toujours avantageux d'avoir au moins une idée approximative du fonctionnement de la récupération de place. .

Note historique: une révision antérieure de la réponse avait une référence incorrecte à l' deleteopérateur. En JavaScript, l' deleteopérateur supprime une propriété d'un objet et est complètement différent de deleteC / C ++.

Noldorin
la source
27
le guide Apple est défectueux: l'auteur utilise deleteincorrectement; Par exemple, dans le premier exemple, au lieu de delete foo, vous devez d'abord supprimer l'écouteur d'événement via window.removeEventListener(), puis utiliser foo = nullpour remplacer la variable; dans IE, delete window.foo(mais pas delete foo) aurait également fonctionné s'il fooétait global, mais même dans FF ou Opera
Christoph
3
Sachez que l'article d'Eric doit être considéré "à des fins historiques uniquement". Mais c'est quand même informatif.
Peter Ivan
2
Notez également - IE 6 et 7 N'UTILISEZ PAS un ramasse-miettes non générationnel. Ils utilisent un collecteur de déchets de comptage de référence simple, qui est plus vulnérable aux problèmes de référence circulaire avec la récupération de place.
Doug
1
ECMAScript deleteest un opérateur unaire (une expression), pas une instruction (ie:) delete 0, delete 0, delete 3. Il ressemble à une déclaration lorsqu'il est exprimé par une déclaration d'expression.
Hydroper
Oui, la réponse à l'époque est maintenant dépassée, depuis 2012, les navigateurs modernes utilisent un algorithme de marquage / balayage .. donc il ne dépend plus de la portée. Référencement: developer.mozilla.org/en-US/docs/Web/JavaScript/…
sksallaj
52

Méfiez-vous des références circulaires lorsque des objets DOM sont impliqués:

Modèles de fuite de mémoire en JavaScript

Gardez à l'esprit que la mémoire ne peut être récupérée qu'en l'absence de références actives à l'objet. Il s'agit d'un piège courant avec les fermetures et les gestionnaires d'événements, car certains moteurs JS ne vérifient pas quelles variables sont réellement référencées dans les fonctions internes et conservent simplement toutes les variables locales des fonctions englobantes.

Voici un exemple simple:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Une implémentation JS naïve ne peut pas être collectée bigStringtant que le gestionnaire d'événements est présent. Il existe plusieurs façons de résoudre ce problème, par exemple la définition bigString = nullà la fin de init()( deletene fonctionnera pas pour les variables locales et les arguments de fonction: deletesupprime les propriétés des objets et l'objet variable est inaccessible - ES5 en mode strict lancera même un ReferenceErrorsi vous essayez pour supprimer une variable locale!).

Je recommande d'éviter autant que possible les fermetures inutiles si vous vous souciez de la consommation de mémoire.

Christoph
la source
20
Le bogue de référence circulaire DOM est spécifique à JScript - aucun autre navigateur ne le souffre sauf IE. En fait, je suis assez sûr que la spécification ECMAScript indique explicitement que le GC doit être capable de gérer de tels cycles: - /
olliej
@olliej: Je ne vois aucune mention du GC dans la spécification ECMAScript .
Janus Troelsen
16

Bonne citation tirée d'un blog

Le composant DOM est "récupéré", tout comme le composant JScript, ce qui signifie que si vous créez un objet dans l'un des composants, puis perdez la trace de cet objet, il sera finalement nettoyé.

Par exemple:

function makeABigObject() {
var bigArray = new Array(20000);
}

Lorsque vous appelez cette fonction, le composant JScript crée un objet (nommé bigArray) accessible dans la fonction. Cependant, dès que la fonction revient, vous "perdez la trace" de bigArray car il n'y a plus moyen de s'y référer. Eh bien, le composant JScript se rend compte que vous en avez perdu la trace, et donc bigArray est nettoyé - sa mémoire est récupérée. Le même genre de chose fonctionne dans le composant DOM. Si vous dites document.createElement('div'), ou quelque chose de similaire, le composant DOM crée un objet pour vous. Une fois que vous perdez la trace de cet objet d'une manière ou d'une autre, le composant DOM nettoiera le connexe.

TStamper
la source
13

Au meilleur de ma connaissance, les objets JavaScript sont régulièrement récupérés quand il n'y a plus de références à l'objet. C'est quelque chose qui se produit automatiquement, mais si vous voulez en savoir plus sur son fonctionnement, au niveau C ++, il est logique de jeter un œil au code source WebKit ou V8

En règle générale, vous n'avez pas besoin d'y penser, cependant, dans les navigateurs plus anciens, comme IE 5.5 et les premières versions d'IE 6, et peut-être les versions actuelles, les fermetures créeraient des références circulaires qui, si elles n'étaient pas cochées, finiraient par consommer de la mémoire. Dans le cas particulier que je veux dire sur les fermetures, c'est lorsque vous avez ajouté une référence JavaScript à un objet dom et un objet à un objet DOM qui renvoyait à l'objet JavaScript. Fondamentalement, il ne pourrait jamais être collecté et entraînerait finalement l'instabilité du système d'exploitation dans les applications de test qui faisaient une boucle pour créer des plantages. En pratique, ces fuites sont généralement petites, mais pour garder votre code propre, vous devez supprimer la référence JavaScript à l'objet DOM.

Habituellement, c'est une bonne idée d'utiliser le mot clé delete pour déréférencer immédiatement les gros objets tels que les données JSON que vous avez reçues et faire tout ce que vous devez en faire, en particulier dans le développement Web mobile. Cela provoque le prochain balayage du GC pour supprimer cet objet et libérer sa mémoire.

Heat Miser
la source
Le problème de référence circulaire JavaScript -> DOM -> JavaScript est-il résolu dans les nouvelles versions d'IE? Si oui, depuis quand? Je pensais que c'était architecturalement très profond et qu'il était peu probable qu'il soit réparé. Avez-vous des sources?
erikkallen
Juste anecdotique. Je n'ai pas remarqué les fuites folles dans IE 8 fonctionnant en mode standard, pas le mode cassé. Je vais ajuster ma réponse.
Heat Miser
1
@erikkallen: oui, le bogue GC a été corrigé dans les versions IE 8+, car les plus anciennes utilisaient un algorithme de récupération de place très naïf, ce qui rendait impossible le GC d'une paire d'objets se référant les uns aux autres. Les mark-and-sweepalgorithmes de style plus récents s'en occupent .
kumarharsh
6

garbage collection (GC) est une forme de gestion automatique de la mémoire en supprimant les objets qui ne sont plus nécessaires.

tout processus traitant de la mémoire, procédez comme suit:

1 - allouez l'espace mémoire dont vous avez besoin

2 - faire un peu de traitement

3 - Libérez cet espace mémoire

il existe deux algorithmes principaux utilisés pour détecter quels objets ne sont plus nécessaires.

Collecte des ordures de comptage de références : cet algorithme réduit la définition de "un objet n'est plus nécessaire" à "un objet n'a aucun autre objet s'y référant", l'objet sera supprimé s'il n'y a pas de point de référence

Algorithme de marquage et de balayage : connectez chaque objet à la source racine. aucun objet ne se connecte à root ou à un autre objet. cet objet sera supprimé.

actuellement les navigateurs les plus modernes utilisant le deuxième algorithme.

Ahmed Gaber - Biga
la source
1
Et pour en ajouter une source, consultez le MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos
4

"En informatique, le garbage collection (GC) est une forme de gestion automatique de la mémoire. Le garbage collector, ou simplement le collector, tente de récupérer les déchets, ou la mémoire utilisée par des objets qui ne seront plus jamais accédés ou mutés par l'application."

Tous les moteurs JavaScript ont leurs propres récupérateurs de place et peuvent différer. La plupart du temps, vous n'avez pas à vous occuper d'eux car ils font simplement ce qu'ils sont censés faire.

L'écriture d'un meilleur code dépend principalement de votre connaissance des principes de programmation, du langage et de l'implémentation particulière.

mtasic85
la source
1

Qu'est-ce que la récupération de place JavaScript?

vérifier ça

Qu'est-ce qui est important pour un programmeur Web de comprendre le garbage collection JavaScript, afin d'écrire un meilleur code?

En Javascript, vous ne vous souciez pas de l'allocation de mémoire et de la désallocation. L'ensemble du problème est demandé à l'interpréteur Javascript. Des fuites sont toujours possibles en Javascript, mais ce sont des bugs de l'interpréteur. Si vous êtes intéressé par ce sujet, vous pouvez en lire plus sur www.memorymanagement.org

dfa
la source
Lequel des divers systèmes de gestion de la mémoire de l'article auquel vous vous connectez est celui utilisé par JavaScript? "Les fuites sont toujours possibles en Javascript, mais ce sont des bugs de l'interprète." - Cela ne signifie pas que les programmeurs JS peuvent simplement ignorer tout le problème, par exemple, il existe un problème de référence circulaire JS <-> DOM assez connu dans les anciennes versions d'IE que vous pouvez contourner dans votre code JS. En outre, le fonctionnement des fermetures JS est une fonctionnalité de conception, pas un bug, mais vous pouvez attacher des morceaux de mémoire plus importants que prévu si vous utilisez des fermetures "de manière inappropriée" (je ne dis pas de ne pas les utiliser).
nnnnnn
3
Les fuites de mémoire sont une bête en JavaScript. Si vous écrivez une simple application "projet de collège", alors ne vous inquiétez pas. Mais lorsque vous commencez à écrire des applications de haute performance au niveau de l'entreprise, la gestion de la mémoire en JavaScript est un must.
Doug
1

Sur Windows, vous pouvez utiliser Drip.exe pour trouver des fuites de mémoire ou vérifier si votre routine mem gratuite fonctionne.

C'est vraiment simple, il suffit d'entrer une URL de site Web et vous verrez la consommation de mémoire du moteur de rendu IE intégré. Appuyez ensuite sur Actualiser, si la mémoire augmente, vous avez trouvé une fuite de mémoire quelque part sur la page Web. Mais cela est également très utile pour voir si les routines de libération de mémoire fonctionnent pour IE.

powtac
la source
1

Les types de référence ne stockent pas l'objet directement dans la variable à laquelle il est affecté, de sorte que la variable objet dans cet exemple ne contient pas réellement l'instance d'objet. Au lieu de cela, il contient un pointeur (ou une référence) vers l'emplacement en mémoire où l'objet existe

var object = new Object();

si vous affectez une variable à une autre, chaque variable obtient une copie du pointeur et toutes deux font toujours référence au même objet en mémoire.

var object1 = new Object();
var object2 = object1;

Deux variables pointant vers un objet

JavaScript est un langage récupéré , vous n'avez donc pas vraiment à vous soucier des allocations de mémoire lorsque vous utilisez des types de référence. Cependant, il est préférable de déréférencer les objets dont vous n'avez plus besoin pour que le garbage collector puisse libérer cette mémoire. La meilleure façon de procéder consiste à définir la variable objet sur null.

var object1 = new Object();
// do something
object1 = null; // dereference

Le déréférencement d' objets est particulièrement important dans les très grandes applications qui utilisent des millions d'objets.

des principes du JavaScript orienté objet - NICHOLAS C. ZAKAS

Ani Naslyan
la source