możliwe wykrycie wycieku pamięci EventEmitter

231

Otrzymuję następujące ostrzeżenie:

(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)

Napisałem taki kod w server.js:

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

Jak to naprawić?

Riz
źródło
46
Służy process.on('warning', e => console.warn(e.stack));do debugowania ostrzeżenia. Nie używaj, process.setMaxListeners(0);ponieważ ostrzeżenie jest z jakiegoś powodu.
Shwetabh Shekhar
Dziękuję Ci. bardzo przydatna instrukcja.
Abdullah Al Farooq,
ten błąd zdarza mi się na yarn install. gdzie mogę umieścić tę linię, aby dodać śledzenie stosu?
Sonic Soul

Odpowiedzi:

94

Jest to wyjaśnione w dokumentacji węzła eventEmitter

Co to za wersja Node? Jaki masz inny kod? To nie jest normalne zachowanie.

Krótko mówiąc: process.setMaxListeners(0);

Zobacz także: node.js - żądanie - jak „emitter.setMaxListeners ()”?

Corey Richardson
źródło
1
v0.6.11 ... Zrobiłem wszystko, ale ostrzeżenie wciąż tam jest. :(
Riz
5
Używamprocess.on('uncaughtException', callback);
Riz
9
process.setMaxListeners(0); // OMG, its so simple... :D
Riz
11
Nie usunęłbym maksymalnego limitu słuchacza. Nie dostaniesz ostrzeżeń, ale dostaniesz wycieki pamięci.
15
W jaki sposób ta odpowiedź uzyskała wszystkie te głosy i została wybrana jako poprawna odpowiedź? nawet jeśli powinno to działać, ale to całkowicie źle !!
ProllyGeek
204

Chciałbym tutaj podkreślić, że to ostrzeżenie jest nie bez powodu i istnieje duża szansa, że ​​poprawka nie zwiększa limitu, ale zastanawia się, dlaczego dodajesz tak wielu słuchaczy do tego samego wydarzenia. Zwiększ limit tylko, jeśli wiesz, dlaczego dodaje się tak wielu słuchaczy i masz pewność, że naprawdę tego chcesz.

Znalazłem tę stronę, ponieważ dostałem to ostrzeżenie, aw moim przypadku wystąpił błąd w używanym przeze mnie kodzie, który zamieniał obiekt globalny w EventEmitter! Z pewnością odradzam globalne zwiększenie limitu, ponieważ nie chcesz, aby te rzeczy pozostały niezauważone.

voltrevo
źródło
14
+1. Zgoda. Ostrzeżenie wskazuje na potencjalny stan wycieku i bezmyślne zwiększenie maxListenerów niekoniecznie naprawi problem. jongleberry.com/understanding-possible-eventemitter-leaks.html
Jeremiah Adams
3
W jaki sposób można debugować „Ostrzeżenie: wykryto przeciek pamięci EventEmitter. Dodano 11 detektorów błędów. Użyj emitter.setMaxListeners (), aby zwiększyć limit”. Czego powinniśmy szukać?
Phil
2
Ale przy tym komunikacie o błędzie nie ma śladu stosu ani nigdzie kodu. Dostaję duże W i P na „Warning” i „Possible”, więc myślę, że może to być inny błąd. Potrzebuję odsłuchania więcej niż jednego zdarzenia, ale zawsze dzwonię do .on we wszystkich przypadkach, więc nie jestem pewien, na czym polega problem.
Phil
2
@ Phil_1984_ Czy znalazłeś rozwiązanie? jeśli nie to wydaje się działać - stackoverflow.com/questions/38482223/...
Yoni Jah
3
Do Twojej wiadomości, link pierwszego komentarza (jongleberry.com) jest offline. Oto zarchiwizowana wersja: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Jeff Ward
76

Domyślnie dla każdego pojedynczego zdarzenia można zarejestrować maksymalnie 10 słuchaczy.

Jeśli to twój kod, możesz określić maxListeners poprzez:

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

Ale jeśli nie jest to Twój kod, możesz użyć tej sztuczki, aby globalnie zwiększyć domyślny limit:

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

Oczywiście możesz wyłączyć limity, ale bądź ostrożny:

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

BTW. Kod powinien znajdować się na samym początku aplikacji.

DODAJ: Od węzła 0.11 ten kod działa również w celu zmiany domyślnego limitu:

require('events').EventEmitter.defaultMaxListeners = 0
zag2art
źródło
5
To było jedyne rozwiązanie, które działało dla mnie w węźle 5.6.0. Wielkie dzięki!
Andrew Faulkner,
Używam Reaktywny, węzeł w wersji 8. *. *. To mi nie zadziałało.
Thomas Valadez
moje było wymagane („zdarzenia”). EventEmitter.defaultMaxListeners = Nieskończoność;
Karl Anthony Baluyot
73

Przyjęta odpowiedź zawiera semantykę dotyczącą tego, jak zwiększyć limit, ale jak zauważył @voltrevo, ostrzeżenie istnieje z jakiegoś powodu i twój kod prawdopodobnie ma błąd.

Rozważ następujący kod błędu:

//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');
}

Teraz obserwuj poprawny sposób dodawania detektora:

//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');
}

Wyszukaj podobne problemy w kodzie przed zmianą maxListeners (co wyjaśniono w innych odpowiedziach)

Rayee Roded
źródło
13
ta odpowiedź powinna zostać zaakceptowana, ponieważ pokazuje rzeczywisty powód ostrzeżenia i sposób jego rozwiązania, +1
Ganesh Karewad 15.01.18
To jest PRAWIDŁOWA ODPOWIEDŹ! Szczerze uważam, że ostrzeżenie maxListener pojawia się głównie z powodu niektórych błędnych kodów. W moim przypadku był to kod mysql. Spróbuję udzielić odpowiedzi, aby wyjaśnić to.
Adrian
25

Zastąpić .on() się once(). Użycie once()usuwa detektory zdarzeń, gdy zdarzenie jest obsługiwane przez tę samą funkcję.

Jeśli to nie rozwiąże problemu, zainstaluj ponownie Restler z tym w pakiecie.json „Restler”: „git: //github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d”

Ma to związek z niewłaściwym zachowaniem Restlera 0.10 w węźle. problem można zobaczyć na git tutaj: https://github.com/danwrong/restler/issues/112 Jednak npm jeszcze tego nie zaktualizował, dlatego powinieneś odwołać się do głowy gita.

Davis Dulin
źródło
to naprawiło ten błąd w moim kodzie przy użyciu frameworka Puppeterr
C Alonso C Ortega
5

Otrzymuję to ostrzeżenie także podczas instalacji aglio na moim Mac OSX.

Używam cmd to naprawić.

sudo npm install -g npm@next

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

Legolas Bloom
źródło
4

Wersja węzła: v11.10.1

Komunikat ostrzegawczy ze śledzenia stosu:

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)

Po wyszukaniu problemów z githubem, dokumentacji i utworzeniu podobnych przecieków pamięci emitera zdarzeń, ten problem został zaobserwowany ze względu na moduł węzła apn używany do powiadomień push iOS.

To rozwiązało:

Należy utworzyć tylko jednego dostawcę na proces dla każdej pary certyfikatów / kluczy. Nie trzeba tworzyć nowego dostawcy dla każdego powiadomienia. Jeśli wysyłasz powiadomienia tylko do jednej aplikacji, nie potrzebujesz więcej niż jednego dostawcy.

Jeśli ciągle tworzysz instancje dostawcy w swojej aplikacji, pamiętaj, aby wywołać Provider.shutdown () po zakończeniu pracy z każdym dostawcą, aby zwolnić jego zasoby i pamięć.

Tworzyłem obiekt dostawcy za każdym razem, gdy wysyłano powiadomienie i oczekiwałem, że gc je usunie.

Sandeep PC
źródło
2

W moim przypadku wywołano go, child.stderr.pipe(process.stderr)gdy inicjowałem 10 (lub mniej więcej) przypadków dziecka. Wszystko, co prowadzi do dołączenia procedury obsługi zdarzeń do tego samego obiektu EventEmitter w pętli, powoduje, że nodejs zgłasza ten błąd.

Vikas Gautam
źródło
2

Czasami te ostrzeżenia pojawiają się, gdy nie jest to coś, co zrobiliśmy, ale coś, o czym zapomnieliśmy!

To ostrzeżenie napotkałem, gdy zainstalowałem pakiet dotenv z npm, ale został przerwany, zanim zacząłem dodawać instrukcję request ('dotenv'). Load () na początku mojej aplikacji. Gdy wróciłem do projektu, zacząłem otrzymywać ostrzeżenia „Wykryto przeciek pamięci potencjalnego zdarzenia”.

Zakładałem, że problem dotyczy czegoś, co zrobiłem, a nie czegoś, czego nie zrobiłem!

Po odkryciu przeoczenia i dodaniu instrukcji wymaganej ostrzeżenie o wycieku pamięci zniknęło.

Motate
źródło
2

Wolę wytropić i naprawić problemy, zamiast tłumić dzienniki, gdy tylko jest to możliwe. Po kilku dniach obserwowania tego problemu w mojej aplikacji, zdałem sobie sprawę, że ustawiam słuchaczy req.socketw oprogramowaniu pośrednim Express, aby wychwytywać błędy gniazd IO, które ciągle się pojawiały. W pewnym momencie dowiedziałem się, że nie jest to konieczne, ale i tak trzymałem słuchaczy w pobliżu. Właśnie je usunąłem, a występujący błąd zniknął. Zweryfikowałem, że to była przyczyna, uruchamiając żądania na mój serwer z następującym oprogramowaniem pośrednim i bez niego:

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();
    }

Usunięcie tego oprogramowania pośredniego zatrzymało wyświetlane ostrzeżenie. Rozejrzę się dookoła twojego kodu i spróbuję znaleźć gdziekolwiek możesz skonfigurować słuchaczy, których nie potrzebujesz.

lwdthe1
źródło
1

Miałem ten sam problem. problem został spowodowany, ponieważ słuchałem portu 8080 na 2 słuchaczach.

setMaxListeners() działa dobrze, ale nie poleciłbym tego.

poprawnym sposobem jest sprawdzenie kodu pod kątem dodatkowych detektorów, usunięcie detektora lub zmiana numeru portu, na którym nasłuchujesz, to naprawiło mój problem.

Noman Abid
źródło
1

Miałem to do dziś, kiedy zaczynam grunt watch. Wreszcie rozwiązany przez

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

Irytująca wiadomość zniknęła.

Ariful Haque
źródło
1

Musisz wyczyścić wszystkich detektorów przed utworzeniem nowych za pomocą:

Klient / Serwer

socket.removeAllListeners(); 

Zakładając, że gniazdo jest gniazdem klienta / lub utworzonym gniazdem serwera.

Możesz także zasubskrybować określone detektory zdarzeń, takie jak na przykład usunięcie connectdetektora w następujący sposób:

this.socket.removeAllListeners("connect");
ProllyGeek
źródło
0

Powiedziałeś, że używasz process.on('uncaughtException', callback);
Gdzie wykonujesz to oświadczenie? Czy jest to przekazane do wywołania zwrotnego http.createServer?
Jeśli tak, inna kopia tego samego wywołania zwrotnego zostanie dołączona do zdarzenia uncaughtException przy każdym nowym żądaniu, ponieważ function (req, res) { ... }zostanie ona wykonana za każdym razem, gdy pojawi się nowe żądanie, podobnie jak instrukcja process.on('uncaughtException', callback);
Zwróć uwagę, że obiekt procesu jest globalny dla wszystkich twoich żądań i dodania detektorów jego wydarzenie za każdym razem, gdy pojawi się nowe żądanie, nie będzie miało sensu. Możesz nie chcieć takiego zachowania.
Jeśli chcesz dołączyć nowego detektora do każdego nowego żądania, powinieneś usunąć wszystkich poprzednich detektorów dołączonych do zdarzenia, ponieważ nie będą już potrzebne przy użyciu:
process.removeAllListeners('uncaughtException');

Monish Chhadwa
źródło
0

Rozwiązaniem naszego zespołu było usunięcie ścieżki rejestru z naszego pliku .npmrc. W pliku rc mieliśmy dwa aliasy ścieżek, a jeden wskazywał na przestarzałą instancję Artifactory.

Błąd nie miał nic wspólnego z rzeczywistym kodem naszej aplikacji, ale wszystko, co dotyczy naszego środowiska programistycznego.

RossO
źródło
0

Miałem do czynienia z tym samym problemem, ale z powodzeniem poradziłem sobie z asynchronicznym oczekiwaniem.
Sprawdź, czy to pomaga.

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

Po:
  for (niech i = 0; i <dataLength; i ++) {
      czekaj na sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

Vivek Mehta
źródło
0

Dzięki RLaaa za pomysł, jak rozwiązać prawdziwy problem / podstawową przyczynę ostrzeżenia. W moim przypadku był to kod buggy MySQL.

Pod warunkiem, że napisałeś obietnicę z kodem wewnątrz:

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)
  })
})

Zauważ, że conn.on('error')w kodzie jest detektor. Ten kod dosłownie dodający detektor w kółko zależy od tego, ile razy wywołujesz zapytanie. Tymczasem if(err) reject(err)robi to samo.

Więc usunąłem conn.on('error')słuchacza i voila ... rozwiązane! Mam nadzieję, że to ci pomoże.

Adrian
źródło
-4

Umieść to w pierwszym wierszu pliku server.js (lub cokolwiek, co zawiera główną aplikację Node.js):

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

i błąd znika :)

Sebastian
źródło
Pomyślałeś, aby umieścić go w głównym pliku i zadziałało. Po prostu umieszczałem to w niewłaściwym miejscu. Dzięki!
sklimkovitch