Fuites de mémoire et fermetures en JavaScript-quand et pourquoi?

vous lisez souvent sur le web que l'utilisation de fermetures est une source massive de fuites de mémoire en JavaScript. La plupart du temps, ces articles font référence au code de script de mélange et aux événements DOM, où le script pointe vers le DOM et vice-versa.

je comprends que la fermeture peut être un problème.

mais Qu'en est-il du noeud?js? Ici, nous n'avons naturellement pas de DOM - il n'y a donc aucune chance d'avoir des effets secondaires de fuite de mémoire comme dans les navigateurs.

quels autres problèmes peut-il y avoir avec les fermetures? Est-ce que quelqu'un peut développer ou m'indiquer un bon tutoriel à ce sujet?

veuillez noter que cette question vise explicitement le noeud.js, et pas le navigateur.

50
demandé sur Golo Roden 2013-10-23 23:04:16

3 réponses

Cette question , demande à propos de quelque chose de similaire. Fondamentalement, l'idée est que si vous utilisez une fermeture dans un rappel, vous devriez "désabonner" le rappel lorsque vous avez terminé de sorte que le GC sache qu'il ne peut pas être appelé de nouveau. Cela a du sens pour moi; si vous avez une fermeture qui attend juste d'être appelée, le GC aura du mal à savoir que vous en avez fini avec elle. En retirant manuellement la fermeture du mécanisme de rappel, elle devient non référencée et disponible pour la collecte.

aussi, Mozilla a publié un grand article sur la découverte de fuites de mémoire dans le noeud.js code. Je suppose que si vous essayez certaines de leurs stratégies, vous pouvez trouver des pièces de votre code qui expriment qui fuit comportement. Les pratiques exemplaires sont agréables et tout, mais je pense qu'il est plus utile de comprendre les besoins de votre programme et de trouver des pratiques exemplaires personnalisées basées sur ce que vous pouvez observer empiriquement.

voici un bref extrait de L'article de Mozilla:

  • Jimb Esser's node-mtrace , qui utilise L'utilitaire GCC mtrace pour profiler l'utilisation des tas.
  • node-heap-dump de Dave Pacheco prend un instantané du tas de V8 et sérialise le tout dans un énorme fichier JSON. Il comprend des outils pour parcourir et examiner le snapshot résultant en JavaScript.
  • Danny Coate's v8-profiler et node-inspector fournissent des reliures de noeuds pour le profileur V8 et une interface de débogage de noeuds en utilisant L'Inspecteur WebKit.
  • fourche de Felix Gnass de la même que un-disables the retainers graph
  • Felix Geisendörfer's Node Memory Leak Tutorial est une courte et douce explication de la façon d'utiliser le v8-profiler et node-debugger , et est actuellement l'état de l'art pour la plupart des Noeuds.débogage des fuites de mémoire js.
  • plateforme Smartos de Joyent, qui fournit un arsenal d'outils à votre disposition pour le noeud de débogage.js fuites de mémoire

Les réponses à cette question dire que vous pouvez aider à la GC par l'affectation null à la fermeture des variables.

var closureVar = {};
doWork(function callback() {
  var data = closureVar.usefulData;
  // Do a bunch of work
  closureVar = null;
});

toutes les variables déclarées à l'intérieur de une fonction disparaîtra lorsque la fonction retourne, sauf ceux qui sont utilisés dans d'autres fermetures. Dans cet exemple, closureVar doit rester en mémoire jusqu'à ce que callback() soit appelé, mais qui sait quand cela arrivera? Une fois que le callback a été appelé, vous pouvez donner un indice au GC en paramétrant votre variable de fermeture à null.

clause de non-responsabilité : comme vous pouvez le voir dans les commentaires ci-dessous, il ya certains utilisateurs SO qui disent que cette information est hors de date et sans conséquence pour le noeud.js. Je n'ai pas encore de réponse définitive à ce sujet; je ne fais que poster ce que j'ai trouvé sur le web.

36
répondu RustyTheBoyRobot 2017-05-23 12:03:02

vous pouvez trouver un bon exemple et une explication dans ce billet de blog de David Glasser.

Eh bien, voici (j'ai ajouté quelques commentaires):

var theThing = null;
var cnt = 0; // helps us to differentiate the leaked objects in the debugger
var replaceThing = function() {
    var originalThing = theThing;
    var unused = function() {
        if (originalThing) // originalThing is used in the closure and hence ends up in the lexical environment shared by all closures in that scope
            console.log("hi");
        };
        // originalThing = null; // <- nulling originalThing here tells V8 gc to collect it 
        theThing = {
            longStr: (++cnt) + '_' + (new Array(1000000).join('*')),
            someMethod: function() { // if not nulled, original thing is now attached to someMethod -> <function scope> -> Closure
                console.log(someMessage);
            }
        };
    };
setInterval(replaceThing, 1000);

s'il vous plaît essayez-le avec et sans annuler originalThing dans les outils de développement de Chrome (onglet ligne de temps, vue mémoire, enregistrement de clic). Notez que l'exemple ci-dessus s'applique au navigateur et au noeud.js environnements.

crédit aussi et surtout à Vyacheslav Egorov .

10
répondu borisdiakur 2014-03-03 12:38:55

Je ne suis pas d'accord avec les fermetures étant une cause de fuites de mémoire. Cela peut-être vrai pour les versions plus anciennes D'IE en raison de sa collecte des ordures de mauvaise qualité. S'il vous plaît lire cet article de Douglas Crockford, qui indique clairement ce qu'est une fuite de mémoire.

la mémoire qui n'est pas récupérée aurait fuité.

fuites ne sont pas le problème, la collecte efficace des ordures est. Des fuites peuvent se produire dans les applications JavaScript des navigateurs et des serveurs. Prenez V8 comme exemple. Dans le navigateur, la collecte des ordures se produit sur un onglet lorsque vous passez à fenêtre/Onglet différent. La fuite est bouchée au ralenti. Les onglets peuvent être inactifs.

Sur le serveur, les choses ne sont pas si faciles. Des fuites peuvent se produire, mais le GC n'est pas aussi rentable. Les serveurs n'ont pas les moyens de communiquer fréquemment avec le GC, ou son rendement en sera affecté. Quand un processus de noeud atteint un certain usage de mémoire, il coups de pied dans le GC. Les fuites seront ensuite enlevés périodiquement. Mais les fuites peuvent toujours se produire à un rythme plus rapide, provoquant des programmes de plantage.

2
répondu user568109 2013-10-30 14:23:09