web-dev-qa-db-fra.com

NodeJS: Comment déboguer "Fuite de mémoire EventEmitter détectée. 11 écouteurs ajoutés"

Comment puis-je déboguer mon application qui génère cette erreur:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at Socket.EventEmitter.addListener (events.js:160:15)
    at Socket.Readable.on (_stream_readable.js:653:33)
    at Socket.EventEmitter.once (events.js:179:8)
    at TCP.onread (net.js:527:26)

Je n'ai pas pu trouver l'objet supposé qui fuit pour augmenter la limite d'écoute de .setMaxListeners(0);

SOLUTION (de fardjad et jan salawa)

Avec les recherches de jan salawa, j'ai trouvé une bibliothèque de travail ( longjohn ) pour des traces de pile croissantes. Avec la réponse de fardjad, j'ai constaté qu'il nous fallait prototyper EventEmitter.addListener [~ # ~] et [~ # ~] EventEmitter.on.

Avec la solution, je pourrais obtenir cette nouvelle trace:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at EventEmitter.addListener.EventEmitter.on (xxx/main.js:44:15)
    at Readable.on (_stream_readable.js:653:33)
    at ServerResponse.assignSocket (http.js:1072:10)
    at parser.onIncoming (http.js:1979:11)
    at parserOnHeadersComplete (http.js:119:23)
    at socket.ondata (http.js:1912:22)
    at TCP.onread (net.js:510:27)
54
Ifnot

Il s’avère qu’il s’agit d’un bogue dans le noeud nodejs; nous parlons de ce problème ici: https://github.com/joyent/node/issues/5108

Solution pour les serveurs http avec bogues qui jettent un EventEmitter memory leak detected et remplissez la mémoire disponible/les temps de calcul disponibles:

Revenir à la version héritée v0.8.23. (Vous pouvez télécharger et installer/compiler à partir d'ici: http://blog.nodejs.org/2013/04/08/node-v0-8-23-legacy/ )

UPDATE 2018: Je vois plusieurs retours sur ce sujet alors que le problème semble avoir disparu depuis des années. Notez que cette réponse est uniquement pour un serveur http qui fuit construit avec nodejs . Si vous êtes dans un autre scénario, consultez les réponses des autres sur ce fil et ne rétrogradez pas votre version (comme suggéré dans cette réponse), vous perdrez votre temps.

32
Ifnot

Pour moi, il semblerait que votre boucle d'événement soit bloquée. Cela peut se produire si vous effectuez des tâches intensives en CPU dans la boucle d'événements node.js. Vous pouvez utiliser processus enfant pour effectuer une tâche intensive.

Vous pouvez vérifier ce qui bloque node.js en utilisant méthodes suivantes :

  1. Mesure du temps de calcul de chaque appel. Connectez-vous si le temps est compté pour que vous sachiez que l'application fonctionne mal.
  2. Configurez un journal pour que vous sachiez quand quelque chose bloque la boucle
    function timeTick() {
        var startTime = (new Date().getTime());
        function onTick() {
            var interval = (new Date().getTime()) - startTime;
            if(interval > 5)
                console.log('timeTick(): WARNING: interval = ' + interval);
        }
       process.nextTick(onTick);
    }
    setInterval(timeTick, 1000);
  3. Utilisez profile.
  4. Utilisez this pour la journalisation et le profilage. C'est la bibliothèque utilisée dans Nodejits.
12
jan salawa

C'est exactement ce qui m'est arrivé. Pour moi, j'ai imbriqué accidentellement un auditeur d'événement dans un autre écouteur d'événement.

Examinez votre code et assurez-vous de NE PAS disposer de bloc d'écoute d'événement DANS UN autre bloc d'écoute d'événement, par exemple (sauf si vous le faites exprès):

socket.on('data', function(data) {
//code goes here

socket.on('close' , function() {
//code goes here
     });

   });

Dans le mauvais exemple ci-dessus, l'écouteur socket.on ('close') doit être OUTSIDE du bloc socket.on ('data').

Dans mon cas, lorsque j'ai reçu 5 flux de données, l'écouteur socket.on ('close') attend qu'un événement close se produise. Lorsque je ferme une fois, un autre 4ème événement de clôture sera exécuté. Ce n'est clairement pas ce que je voulais. Ceci est dû à la nature de Node.js qui est non bloquante. Il "se souvient" des événements en raison de la fonction de rappel.

10
fadleen

J'ai essayé de prototyper EventEmitter pour ajouter des messages de journal dans addListener mais je ne pouvais pas le faire fonctionner

Pour accrocher addListener, vous pouvez faire quelque chose comme ceci:

// on the first line of your main script
var events = require("events"),
    EventEmitter = events.EventEmitter;

var originalAddListener = EventEmitter.prototype.addListener;
EventEmitter.prototype.addListener = function (type, listener) {
    if (this.listenerCount(this, type) >= 10) {
        // TODO: PLACE YOUR CODE FOR DEBUGGING HERE
    }
    originalAddListener.apply(this, arguments);
}
6
fardjad

Cet avertissement sera émis si vous vous inscrivez à un événement particulier du même objet plus de 11 fois.

Vérifiez si vous rencontrez un appel "on" pour un événement particulier dans une fonction que vous appelez fréquemment. Cela conduit à vous inscrire à un événement plusieurs fois.

Ce le lien m'a aidé à comprendre cela.

5
Vishwanath gowda k

Depuis le noeud 6, vous devriez utiliser node --trace-warnings: https://nodejs.org/api/cli.html#cli_trace_warnings

2
nicojs

Je voyais cela quand j'exécutais mes tests unitaires. Mes tests unitaires ont appelé à plusieurs reprises le code qui appelait:

process.on("uncaughtException", () => { ... });

J'ai dû utiliser l'injection de dépendance pour injecter un faux processus, ce qui a résolu le problème.

main.js:

export function main(myProcess) {
    myProcess.on("uncaughtException", () => { ... });
}

if (require.main === module) { // to prevent this from executing when running unit tests
    main(process);
}

Mes tests unitaires feraient:

const fakeProcess = jasmine.createSpy("process", ["on"]);
main(fakeProcess);
0
pushkin