Möglicher EventEmitter-Speicherverlust erkannt

231

Ich erhalte folgende Warnung:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace: 
    at EventEmitter.<anonymous> (events.js:139:15)
    at EventEmitter.<anonymous> (node.js:385:29)
    at Server.<anonymous> (server.js:20:17)
    at Server.emit (events.js:70:17)
    at HTTPParser.onIncoming (http.js:1514:12)
    at HTTPParser.onHeadersComplete (http.js:102:31)
    at Socket.ondata (http.js:1410:22)
    at TCP.onread (net.js:354:27)

Ich habe folgenden Code in server.js geschrieben:

http.createServer(
    function (req, res) { ... }).listen(3013);

Wie kann ich das beheben?

Riz
quelle
46
Verwenden Sie process.on('warning', e => console.warn(e.stack));diese Option, um die Warnung zu debuggen. Nicht verwenden, process.setMaxListeners(0);da die Warnung aus irgendeinem Grund vorhanden ist.
Shwetabh Shekhar
Danke dir. sehr nützliche Anleitung.
Abdullah Al Farooq
Dieser Fehler passiert mir am yarn install. Wo kann ich diese Zeile einfügen, um eine Stapelverfolgung hinzuzufügen?
Sonic Soul

Antworten:

94

Dies wird in der Dokumentation zum Knoten eventEmitter erläutert

Welche Version von Node ist das? Welchen anderen Code hast du? Das ist kein normales Verhalten.

Kurz gesagt, es ist: process.setMaxListeners(0);

Siehe auch: node.js - request - Wie wird "emitter.setMaxListeners ()"?

Corey Richardson
quelle
1
v0.6.11 ... Ich habe alles getan, aber die Warnung ist immer noch da. :(
Riz
5
Ich benutzeprocess.on('uncaughtException', callback);
Riz
9
process.setMaxListeners(0); // OMG, its so simple... :D
Riz
11
Ich würde das maximale Listener-Limit nicht entfernen. Sie erhalten keine Warnungen, aber Speicherlecks.
15
Wie hat diese Antwort all diese Stimmen erhalten und wurde als richtige Antwort ausgewählt? obwohl es funktionieren sollte, aber das ist völlig falsch !!
ProllyGeek
203

Ich möchte hier darauf hinweisen, dass diese Warnung aus einem bestimmten Grund vorliegt und es eine gute Chance gibt, dass die richtige Lösung nicht vorhanden ist das Limit erhöht, sondern herausfindet, warum Sie dem gleichen Ereignis so viele Hörer hinzufügen. Erhöhen Sie das Limit nur, wenn Sie wissen, warum so viele Hörer hinzugefügt werden, und sicher sind, dass es das ist, was Sie wirklich wollen.

Ich habe diese Seite gefunden, weil ich diese Warnung erhalten habe und in meinem Fall ein Fehler in einem von mir verwendeten Code aufgetreten ist, der das globale Objekt in einen EventEmitter verwandelt hat! Ich würde auf jeden Fall davon abraten, das Limit global zu erhöhen, da Sie nicht möchten, dass diese Dinge unbemerkt bleiben.

Voltrevo
quelle
14
+1. Einverstanden. Die Warnung zeigt einen möglichen Leckzustand an und eine sinnlose Erhöhung der maxListener behebt das Problem nicht unbedingt. jongleberry.com/understanding-possible-eventemitter-leaks.html
Jeremiah Adams
3
Wie können Sie "Warnung: Möglicher EventEmitter-Speicherverlust erkannt. 11 Fehlerlistener hinzugefügt. Verwenden Sie emitter.setMaxListeners (), um das Limit zu erhöhen" debuggen. Was sollen wir suchen?
Phil
2
Es gibt jedoch keine Stapelverfolgung und keinen Code mit dieser Fehlermeldung. Ich bekomme Großbuchstaben W und P für "Warnung" und "Möglich", daher denke ich, dass es sich möglicherweise um einen anderen Fehler handelt. Ich muss mehr als ein Ereignis abhören, aber ich rufe in allen Fällen immer nur einmal an. Ich bin mir also nicht sicher, wo das Problem liegt.
Phil
2
@ Phil_1984_ Hast du eine Lösung gefunden? Wenn nicht, scheint dies zu funktionieren - stackoverflow.com/questions/38482223/…
Yoni Jah
2
Zu Ihrer Information, der Link des ersten Kommentars (jongleberry.com) ist offline. Hier ist die archivierte Version: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Jeff Ward
76

Standardmäßig können maximal 10 Listener für ein einzelnes Ereignis registriert werden.

Wenn es Ihr Code ist, können Sie maxListeners angeben über:

const emitter = new EventEmitter()
emitter.setMaxListeners(100)
// or 0 to turn off the limit
emitter.setMaxListeners(0)

Wenn es sich jedoch nicht um Ihren Code handelt, können Sie den Trick verwenden, um das Standardlimit global zu erhöhen:

require('events').EventEmitter.prototype._maxListeners = 100;

Natürlich können Sie die Grenzen ausschalten, aber seien Sie vorsichtig:

// turn off limits by default (BE CAREFUL)
require('events').EventEmitter.prototype._maxListeners = 0;

Übrigens. Der Code sollte ganz am Anfang der App stehen.

HINZUFÜGEN: Seit Knoten 0.11 ändert dieser Code auch das Standardlimit:

require('events').EventEmitter.defaultMaxListeners = 0
zag2art
quelle
5
Dies war die einzige Lösung, die in Knoten 5.6.0 für mich funktioniert hat. Danke vielmals!
Andrew Faulkner
Ich verwende React-Native, Knotenversion 8. *. *. Das hat bei mir nicht funktioniert.
Thomas Valadez
meins war erforderlich ('events'). EventEmitter.defaultMaxListeners = Infinity;
Karl Anthony Baluyot
72

Die akzeptierte Antwort enthält die Semantik zum Erhöhen des Grenzwerts. Wie @voltrevo jedoch darauf hinwies, gibt es eine Warnung aus einem bestimmten Grund und Ihr Code weist wahrscheinlich einen Fehler auf.

Betrachten Sie den folgenden Buggy-Code:

//Assume Logger is a module that emits errors
var Logger = require('./Logger.js');

for (var i = 0; i < 11; i++) {
    //BUG: This will cause the warning
    //As the event listener is added in a loop
    Logger.on('error', function (err) {
        console.log('error writing log: ' + err)
    });

    Logger.writeLog('Hello');
}

Beobachten Sie nun die richtige Art, den Listener hinzuzufügen:

//Good: event listener is not in a loop
Logger.on('error', function (err) {
    console.log('error writing log: ' + err)
});

for (var i = 0; i < 11; i++) {
    Logger.writeLog('Hello');
}

Suchen Sie nach ähnlichen Problemen in Ihrem Code, bevor Sie die maxListeners ändern (was in anderen Antworten erläutert wird).

Rayee Roded
quelle
13
Diese Antwort sollte akzeptiert werden, da sie den tatsächlichen Grund für die Warnung und deren Lösung zeigt. +1
Ganesh Karewad
Dies ist die richtige Antwort! Ich denke ehrlich, dass die maxListener-Warnung hauptsächlich aufgrund einiger fehlerhafter Codes angezeigt wird. In meinem Fall war es der MySQL-Code. Ich werde versuchen, eine Antwort zu geben, um Klarheit zu schaffen.
Adrian
25

Ersetzen .on()durch once(). Verwenden vononce() wird Ereignis-Listener entfernt, wenn das Ereignis von derselben Funktion behandelt wird.

Wenn dies das Problem nicht behebt, installieren Sie restler damit in Ihrem package.json "restler" neu: "git: //github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d"

Dies hat mit dem Fehlverhalten von Restler 0.10 gegenüber dem Knoten zu tun. Sie können das auf git geschlossene Problem hier sehen: https://github.com/danwrong/restler/issues/112 npm muss dies jedoch noch aktualisieren, weshalb Sie sich auf den git-Kopf beziehen müssen.

Davis Dulin
quelle
Dies behebt diesen Fehler in meinem Code mit dem Puppeterr-Framework
C Alonso C Ortega
5

Ich erhalte diese Warnung auch, wenn ich aglio auf meinem Mac OSX installiere.

Ich benutze cmd fix it.

sudo npm install -g npm@next

https://github.com/npm/npm/issues/13806

Legolas Bloom
quelle
4

Knotenversion: v11.10.1

Warnmeldung vom Stack-Trace:

process.on('warning', e => console.warn(e.stack));
(node:17905) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 wakeup listeners added. Use emitter.setMaxListeners() to increase limit
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 wakeup listeners added. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:255:17)
    at Connection.addListener (events.js:271:10)
    at Connection.Readable.on (_stream_readable.js:826:35)
    at Connection.once (events.js:300:8)
    at Connection._send (/var/www/html/fleet-node-api/node_modules/http2/lib/protocol/connection.js:355:10)
    at processImmediate (timers.js:637:19)
    at process.topLevelDomainCallback (domain.js:126:23)

Nach der Suche nach Github-Problemen, der Dokumentation und dem Erstellen ähnlicher Speicherverluste bei Ereignisemittern wurde dieses Problem aufgrund des für die iOS-Push-Benachrichtigung verwendeten Node-Apn- Moduls beobachtet .

Dies löste es:

Sie sollten nur einen Anbieter pro Prozess für jedes Zertifikat / Schlüsselpaar erstellen, über das Sie verfügen. Sie müssen nicht für jede Benachrichtigung einen neuen Anbieter erstellen. Wenn Sie nur Benachrichtigungen an eine App senden, ist nicht mehr als ein Anbieter erforderlich.

Wenn Sie ständig Provider-Instanzen in Ihrer App erstellen, rufen Sie Provider.shutdown () auf, wenn Sie mit jedem Provider fertig sind, um seine Ressourcen und seinen Speicher freizugeben.

Ich habe jedes Mal, wenn die Benachrichtigung gesendet wurde, ein Provider-Objekt erstellt und erwartet, dass der GC es löscht.

Sandeep PC
quelle
2

In meinem Fall wurde es child.stderr.pipe(process.stderr)aufgerufen, als ich 10 (oder so) Instanzen des Kindes initiierte. Alles, was dazu führt, dass ein Event-Handler in einer LOOP an dasselbe EventEmitter-Objekt angehängt wird, führt dazu, dass nodejs diesen Fehler auslöst.

Vikas Gautam
quelle
2

Manchmal treten diese Warnungen auf, wenn wir nicht etwas getan haben, sondern etwas vergessen haben!

Diese Warnung trat auf, als ich das dotenv-Paket mit npm installierte, wurde jedoch unterbrochen, bevor ich die Anweisung require ('dotenv'). Load () am Anfang meiner App hinzufügte. Als ich zum Projekt zurückkehrte, wurden die Warnungen "Möglicher EventEmitter-Speicherverlust erkannt" angezeigt.

Ich nahm an, dass das Problem von etwas herrührte, das ich getan hatte, nicht von etwas, das ich nicht getan hatte!

Sobald ich mein Versehen entdeckt und die require-Anweisung hinzugefügt hatte, wurde die Speicherverlustwarnung gelöscht.

Motate
quelle
2

Ich ziehe es vor, Probleme zu suchen und zu beheben, anstatt Protokolle zu unterdrücken, wann immer dies möglich ist. Nachdem ich dieses Problem einige Tage lang in meiner App beobachtet hatte, stellte ich fest, dass ich req.socketin einer Express-Middleware Listener für die Socket-Io-Fehler einstellte , die immer wieder auftauchten. Irgendwann habe ich gelernt, dass das nicht nötig ist, aber ich habe die Zuhörer trotzdem behalten. Ich habe sie gerade entfernt und der Fehler, den Sie haben, ist verschwunden. Ich habe überprüft, ob dies die Ursache ist, indem ich Anforderungen an meinen Server mit und ohne die folgende Middleware ausgeführt habe:

socketEventsHandler(req, res, next) {
        req.socket.on("error", function(err) {
            console.error('------REQ ERROR')
            console.error(err.stack)
        });
        res.socket.on("error", function(err) {
            console.error('------RES ERROR')
            console.error(err.stack)
        });
        next();
    }

Durch das Entfernen dieser Middleware wurde die angezeigte Warnung gestoppt. Ich würde mich in Ihrem Code umsehen und versuchen, einen Ort zu finden, an dem Sie möglicherweise Listener einrichten, die Sie nicht benötigen.

lwdthe1
quelle
1

Ich hatte das gleiche Problem. und das Problem wurde verursacht, weil ich Port 8080 auf 2 Listenern hörte.

setMaxListeners() funktioniert gut, aber ich würde es nicht empfehlen.

Der richtige Weg ist, Ihren Code auf zusätzliche Listener zu überprüfen, den Listener zu entfernen oder die Portnummer zu ändern, die Sie abhören. Dies hat mein Problem behoben.

Noman Abid
quelle
1

Ich hatte das bis heute, als ich anfing grunt watch. Endlich gelöst von

watch: {
  options: {
    maxListeners: 99,
    livereload: true
  },
}

Die nervige Nachricht ist weg.

Ariful Haque
quelle
1

Sie müssen alle Listener löschen, bevor Sie neue erstellen können, indem Sie:

Kundenserver

socket.removeAllListeners(); 

Angenommen, Socket ist Ihr Client-Socket oder der erstellte Server-Socket.

Sie können auch bestimmte Ereignis-Listener abonnieren, z. B. den connectListener wie folgt entfernen :

this.socket.removeAllListeners("connect");
ProllyGeek
quelle
0

Sie sagten, Sie verwenden process.on('uncaughtException', callback);
Wo führen Sie diese Anweisung aus? Wird es innerhalb des Rückrufs weitergeleitet http.createServer?
Wenn ja, wird verschiedene Kopie des gleichen Rückrufs an dem beigefügten erhalten uncaughtException bei jeder neuen Anforderung Ereignis, weil die function (req, res) { ... }Ausführung jedes Mal wird eine neue Anforderung kommt und so wird die Aussage zu process.on('uncaughtException', callback);
beachten , dass das Prozessobjekt ist global für alle Ihre Anfragen und das Hinzufügen von Hörern Jedes Mal, wenn eine neue Anfrage eingeht, ergibt dies keinen Sinn. Möglicherweise möchten Sie ein solches Verhalten nicht.
Wenn Sie für jede neue Anforderung einen neuen Listener anhängen möchten, sollten Sie alle vorherigen Listener entfernen, die an das Ereignis angehängt sind, da sie nicht mehr benötigt werden, indem Sie:
process.removeAllListeners('uncaughtException');

Monish Chhadwa
quelle
0

Die Lösung unseres Teams bestand darin, einen Registrierungspfad aus unserer .npmrc zu entfernen. Wir hatten zwei Pfad-Aliase in der RC-Datei, und einer zeigte auf eine veraltete Artifactory-Instanz.

Der Fehler hatte nichts mit dem tatsächlichen Code unserer App zu tun, sondern alles mit unserer Entwicklungsumgebung.

RossO
quelle
0

Ich hatte das gleiche Problem, aber ich habe erfolgreich mit async warten gewartet.
Bitte überprüfen Sie, ob es hilft.

let dataLength = 25;
Vorher:
  for (sei i = 0; i <dataLength; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

After:
  for (sei i = 0; i <dataLength; i ++) {
      warte auf sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }}

Vivek Mehta
quelle
0

Vielen Dank an RLaaa für die Idee, wie ich das eigentliche Problem / die Grundursache der Warnung lösen kann. In meinem Fall war es MySQL-Buggy-Code.

Vorausgesetzt, Sie haben ein Versprechen mit Code geschrieben:

pool.getConnection((err, conn) => {

  if(err) reject(err)

  const q = 'SELECT * from `a_table`'

  conn.query(q, [], (err, rows) => {

    conn.release()

    if(err) reject(err)

    // do something
  })

  conn.on('error', (err) => {

     reject(err)
  })
})

Beachten Sie, dass conn.on('error')der Code einen Listener enthält. Dieser Code, der buchstäblich immer wieder Listener hinzufügt, hängt davon ab, wie oft Sie die Abfrage aufrufen. Inzwischen if(err) reject(err)macht das gleiche.

Also habe ich den conn.on('error')Listener entfernt und voila ... gelöst! Hoffe das hilft dir.

Adrian
quelle
-4

Fügen Sie dies in die erste Zeile Ihrer server.js ein (oder was auch immer Ihre Haupt-Node.js-App enthält):

require('events').EventEmitter.prototype._maxListeners = 0;

und der Fehler verschwindet :)

Sebastian
quelle
Sie haben mir die Idee gegeben, es in eine Hauptdatei zu schreiben, und es hat funktioniert. Ich habe es nur an einen falschen Ort gebracht. Vielen Dank!
Sklimkovitch