8 votes

Collecte d'ordures dans les fermetures JavaScript

J'ai besoin d'aide pour comprendre comment cela fonctionne (ou ne fonctionne pas, d'ailleurs).

  1. Dans une page Web, je crée un écouteur d'événement de clic pour un nœud.
  2. Dans l'écouteur, je crée une instance d'une classe aléatoire, qui définit le nœud comme une propriété en son sein. Ainsi, si var classInstance est l'instance, je peux accéder au noeud comme quelque chose comme classInstance.rootNode .
  3. Quand l'écouteur se déclenche, je prépare une requête ajax, garde classInstance en fermeture et transmet la réponse ajax à classInstance et l'utiliser pour éventuellement modifier le rootNode Le style, le contenu, etc.

Ma question est, une fois que j'en ai fini avec classInstance En supposant que rien d'autre ne le référence et que, seul, il ne contient rien d'autre dans sa propre fermeture, le ramasseur d'ordures s'en débarrassera-t-il ? Si non, comment puis-je le marquer pour qu'il soit éliminé ?

7voto

Elias Van Ootegem Points 29404

En réponse aux doutes de @Beetroot-Beetroot (que j'ai aussi, il faut l'avouer), j'ai creusé un peu plus. J'ai mis en place ce violon et a utilisé le Chronologie des outils de développement du chrome et cet article comme une ligne directrice. Dans le fiddle, deux handlers presque identiques créent une fermeture avec 2 objets date. Le premier ne fait référence qu'à a le second fait référence aux deux a et b . Bien que dans les deux cas, seuls a ne peuvent jamais être réellement exposées (valeurs codées en dur), la première fermeture utilise beaucoup moins de mémoire. Je ne peux pas dire avec certitude si cela est dû à JIC (compilation juste à temps) ou à la magie de l'optimisation JS de V8. Mais d'après ce que j'ai lu, je dirais que c'est la GC de V8 qui désalloue b lorsque le tst retourne, ce qui n'est pas le cas dans le second cas ( bar références b quand tst2 retours). J'ai l'impression que ce n'est pas si farfelu, et je ne serais pas du tout surpris de découvrir que FF et même IE fonctionneraient de la même manière.
J'ai juste ajouté cette mise à jour, qui n'est peut-être pas pertinente, par souci d'exhaustivité et parce que j'ai l'impression qu'un lien vers la documentation de Google sur les outils de développement est une bonne idée. valeur ajoutée en quelque sorte.


Cela dépend en quelque sorte, mais en règle générale, tant que vous ne pouvez pas faire référence à l'option classInstance plus, elle devrait être GC'ed, indépendamment de ses propres références circulaires. J'ai testé un grand nombre de constructions similaires à celle que vous décrivez ici. Peut-être que cela vaut la peine de jeter un coup d'œil
J'ai constaté que les fermetures et les fuites de mémoire ne sont pas si courantes ou faciles à éviter (du moins, plus maintenant).

Mais comme le dit la réponse acceptée : il est pratiquement impossible de savoir quand un code va fuir.
En relisant votre question, je dirais : non, vous ne ferez pas fuir la mémoire : l'option classInstance n'a pas été créée dans la portée globale, mais elle est transmise à diverses fonctions (et donc à diverses portées). Ces scopes se désintègrent chaque fois que la fonction revient. classInstance ne sera pas GC'ed s'il a été passé à une autre fonction/scope. Mais dès que la dernière fonction qui fait référence à classInstance retourne, l'objet est marqué pour la GC. Bien sûr, il peut s'agir d'une référence circulaire, mais c'est une référence à laquelle on ne peut accéder que depuis sa propre portée.
On ne peut pas vraiment appeler cela une fermeture, non plus : les fermetures se produisent quand il y a une forme de exposition à la portée externe, ce qui n'est pas le cas dans votre exemple.

Je ne suis pas doué pour expliquer ce genre de choses, mais récapitulons :

var foo = (function()
{
    var a, b, c, d;
    return function()
    {
        return a;
    }
})();

Le GC va désallouer la mémoire b , c et d référence : ils sont sortis du champ d'application, il n'y a aucun moyen d'y accéder...

var foo = (function()
{
    var a, b, c, d;
    return function()
    {
        a.getB = function()
        {
            return b;
        }
        a.getSelf = function()
        {
            return a;//or return this;
        }
        return a;
    }
})();
//some code
foo = new Date();//

Dans ce cas, b n'aura pas non plus de GC, pour des raisons évidentes. foo expose a et b , donde a est un objet qui contient une référence circulaire. Bien que dès que foo = new Date() , foo perd toute référence à a . Bien sûr, a se réfère toujours à lui-même, mais a n'est plus exposé Il peut faire référence à ce qu'il veut. La plupart des navigateurs s'en moquent et GC a et b . En fait, j'ai vérifié et Chrome, FF, et IE8 tous les GC le code ci-dessus parfaitement ... pas de soucis, alors.

2voto

jrajav Points 2789

Je ne suis pas un expert sur cette question, mais je suis presque sûr que le CG va no s'en débarrasser. En fait, il ne le sera probablement jamais, car vous avez créé une référence circulaire entre l'écouteur d'événements et le nœud du DOM. Pour permettre la collecte des déchets, vous devez définir l'une ou les deux références (l'écouteur d'événements et/ou le rootNode) à undefined ou null.

Néanmoins, je ne m'en préoccuperais que si vous créez un grand nombre de ces classInstances ou si elles peuvent être créées plusieurs fois pendant la durée de vie de la page. Sinon, c'est une optimisation inutile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X