виявлено можливий витік пам'яті EventEmitter


231

Я отримую таке попередження:

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

Я написав такий код у server.js:

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

Як це виправити?


46
Використовуйте process.on('warning', e => console.warn(e.stack));для налагодження попередження. Не використовуйте, process.setMaxListeners(0);оскільки попередження існує з якихось причин.
Шватаб Шехар

Дякую. дуже корисна інструкція.
Абдулла Аль Фарук

ця помилка трапляється зі мною yarn install. куди я можу поставити цей рядок, щоб додати слід стека?
Sonic Soul

Відповіді:


94

Це пояснюється в документації по вузлу eventEmitter

Яка версія Node це? Який ще код у вас є? Це не нормальна поведінка.

Якщо коротко, це: process.setMaxListeners(0);

Також дивіться: node.js - запит - як "emitter.setMaxListeners ()"?


1
v0.6.11 ... Я все зробив, але попередження все ще є. :(
Різ

5
Я використовуюprocess.on('uncaughtException', callback);
Різ

9
process.setMaxListeners(0); // OMG, its so simple... :D
Різ

11
Я не знімав би максимальну кількість слухачів. Ви не отримаєте попереджень, але ви отримаєте витоки пам'яті.

15
Як ця відповідь отримала всі ці голоси, і як вона була обрана як правильна відповідь? хоч це має працювати, але це абсолютно неправильно !!
ProllyGeek

204

Я хотів би зазначити тут, що це попередження є з причини, і є хороший шанс, що правильне виправлення - це не збільшення межі, а з'ясування, чому ви додаєте стільки слухачів до однієї події. Лише збільшуйте ліміт, якщо знаєте, чому так багато слухачів додають і впевнені, що це дійсно ви хочете.

Я знайшов цю сторінку, тому що отримав це попередження, і в моєму випадку з’явилася помилка в деякому коді, який я використовував, який перетворював глобальний об’єкт у EventEmitter! Я, безумовно, радив би не збільшувати ліміт у всьому світі, оскільки ви не хочете, щоб ці речі залишалися непоміченими.


14
+1. Домовились. Попередження вказує на потенційний стан протікання, і безглуздо збільшення макс-слухачів не обов'язково виправить проблему. jongleberry.com/understanding-possible-eventemitter-leaks.html
Єремія Адамс

3
Як можна налагодити "Попередження: Виявлено можливу витік пам'яті EventEmitter. Додано 11 слухачів помилок. Використовуйте emitter.setMaxListeners () для збільшення ліміту". Що ми повинні шукати?
Філ

2
Але немає ні сліду стека, ні коду з цим повідомленням про помилку. Я отримую капітал W і P на "Попередження" та "Можливо", тому я думаю, що це може бути інша помилка. Мені потрібно прослухати більше однієї події, але я завжди колись дзвоню .on у всіх випадках, тож не знаю, у чому проблема.
Філ

2
@ Phil_1984_ Ви знайшли рішення? якщо ні, здається, це працює - stackoverflow.com/questions/38482223/…
Yoni Jah

3
FYI, посилання першого коментаря (jongleberry.com) офлайн. Ось заархівована версія: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Jeff Ward

76

За замовчуванням на будь-яку окрему подію може бути зареєстровано максимум 10 слухачів.

Якщо це ваш код, ви можете вказати maxListeners за допомогою:

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

Але якщо це не ваш код, ви можете використовувати трюк для збільшення ліміту за замовчуванням у всьому світі:

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

Звичайно, ви можете вимкнути межі, але будьте обережні:

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

До речі. Код має бути на самому початку програми.

ДОДАТИ: Оскільки вузол 0,11, цей код також працює для зміни ліміту за замовчуванням:

require('events').EventEmitter.defaultMaxListeners = 0

5
Це єдине рішення, яке працювало для мене в Node 5.6.0. Дякую тонну!
Ендрю Фолкнер

Я використовую рідну реакцію, версію вузла 8. *. *. Це не спрацювало для мене.
Томас Валадес

мій був вимагати ("події"). EventEmitter.defaultMaxListeners = Нескінченність;
Карл Ентоні Балуйот

73

Прийнята відповідь надає семантику щодо того, як збільшити ліміт, але, як @voltrevo зазначав, що попередження існує з причини, і ваш код, ймовірно, має помилку.

Розглянемо наступний баггі-код:

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

Тепер дотримуйтесь правильного способу додавання слухача:

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

Шукайте подібні проблеми у своєму коді, перш ніж змінювати maxListeners (що пояснюється в інших відповідях)


13
цю відповідь слід сприйняти, оскільки вона показує фактичну причину попередження та шляхи її вирішення, +1
Ганеш Каревад

Це ПРАВИЛЬНИЙ ВІДПОВІДЬ! Я чесно думаю, що попередження maxListener з'являється здебільшого через деякі помилки. У моєму випадку це був код mysql. Я спробую дати відповідь лише для того, щоб надати зрозумілість для цього.
Адріан

25

Замініть .on()на once(). Використанняonce() видаляє слухачів подій, коли подія обробляється тією ж функцією.

Якщо цього не виправити, перевстановіть restler з цим у своєму package.json "restler": "git: //github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d"

Це пов'язано з неправильним поводженням з вузлом restler 0.10. Ви можете побачити проблему, закриту на git тут: https://github.com/danwrong/restler/isissue/112 Однак, npm ще не оновлював це, тому вам потрібно посилатися на голову git.


виправити цю помилку в моєму коді за допомогою Puppeterr Framework
C Alonso C Ortega


4

Версія вузла: v11.10.1

Попереджувальне повідомлення від сліду стека:

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)

Після пошуку випусків, документації та створення схожих витоків пам'яті випромінювача подій, ця проблема спостерігалася через node-apn модулю який використовується для натискання на iOS.

Це вирішило це:

Вам слід створити лише одного постачальника за кожну процедуру для кожної пари сертифікатів / ключів. Вам не потрібно створювати нового Постачальника для кожного повідомлення. Якщо ви надсилаєте повідомлення лише одному додатку, більше одного постачальника не потрібно.

Якщо ви постійно створюєте екземпляри постачальника у своєму додатку, обов’язково зателефонуйте на Provider.shutdown (), коли ви завершите роботу з кожним постачальником, щоб звільнити його ресурси та пам'ять.

Я створював об’єкт провайдера щоразу, коли повідомлення надсилалося, і очікував, що gc очистить його.


2

У моєму випадку це було child.stderr.pipe(process.stderr)те, що викликали, коли я ініціював 10 (або близько того) випадків дитини. Так що все, що призводить до приєднання обробника подій до того ж Об'єкта EventEmitter у LOOP, викликає nodejs, щоб кинути цю помилку.


2

Іноді ці застереження виникають, коли ми це не щось зробили, а щось, що ми забули зробити!

З цим попередженням я зіткнувся, коли встановив пакунок dotenv з npm, але його перервали, перш ніж я почав додавати заяву Requ ('dotenv'). Load () на початку свого додатка. Повернувшись до проекту, я почав отримувати попередження "Можливий вибій пам'яті EventEmitter".

Я припускав, що проблема полягає в тому, що я зробив, а не в тому, чого я не робив!

Як тільки я виявив свій нагляд і додав заяву про вимогу, попередження про витоку пам’яті знімається.


2

Я вважаю за краще шукати і виправляти проблеми, а не придушувати журнали, коли це можливо. Після кількох днів спостереження за цією проблемою в моєму додатку я зрозумів, що встановлюю слухачів на req.socketпрограмне забезпечення Express в середньому програмному забезпеченні, щоб виявити помилки socket io, які постійно з'являлися. У якийсь момент я дізнався, що це не потрібно, але я все одно тримав слухачів. Я просто їх видалив, і помилка, яку ви відчуваєте, пішла. Я перевірив, що це було причиною запуску запитів на мій сервер із та без наступного проміжного програмного забезпечення:

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

Видалення цього програмного забезпечення зупинило попередження, яке ви бачите. Я б озирнувся з вашим кодом і спробував би знайти там, де ви, можливо, налаштовуєте слухачів, які вам не потрібні.


1

у мене була така ж проблема. і проблема була викликана тим, що я слухав порт 8080 на 2 слухачах.

setMaxListeners() працює чудово, але я б не рекомендував це.

правильний спосіб - перевірити свій код на додаткових слухачів, видалити слухача або змінити номер порту, на якому ви слухаєте, це вирішило мою проблему.


1

Я мав це до сьогодні, коли починаю grunt watch. Нарешті вирішено

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

Прикрого повідомлення пропало.


1

Потрібно очистити всіх слухачів, перш ніж створювати нових, використовуючи:

Клієнт / сервер

socket.removeAllListeners(); 

Припустимо, сокет - це ваш клієнтський сокет / або створений серверний сокет.

Ви також можете підписатися на певних слухачів подій, наприклад, наприклад, видалити такого connectслухача:

this.socket.removeAllListeners("connect");

0

Ви сказали, що використовуєте process.on('uncaughtException', callback);
Де ви виконуєте цю заяву? Чи передається це в межах зворотного дзвінка http.createServer?
Якщо так, то різні копії одного і того ж зворотного дзвінка будуть приєднуються до події uncaughtException при кожному новому запиті, тому що function (req, res) { ... }виконується кожен раз, коли надходить новий запит, і так буде твердження. process.on('uncaughtException', callback);
Зауважте, що об'єкт процесу є глобальним для всіх ваших запитів та додавання слухачів до його події щоразу, коли надходить новий запит, не буде мати сенсу. Можливо, ви не хочете такої поведінки.
Якщо ви хочете приєднати нового слухача до кожного нового запиту, слід видалити всіх попередніх слухачів, приєднаних до події, оскільки вони більше не потрібні, використовуючи:
process.removeAllListeners('uncaughtException');


0

Виправленням нашої команди для цього було видалення шляху реєстру з нашого .npmrc. У нас було два псевдоніми файлів у файлі rc, а один вказував на застарілий екземпляр Artifactory.

Помилка не мала нічого спільного з фактичним кодом нашого додатка, але все, що стосується нашого середовища розробки.


0

Я зіткнувся з тим же питанням, але я успішно впорався з асинхронізацією.
Перевірте, чи допомагає це.

нехай dataLength = 25;
Перед:
  для (нехай i = 0; i <dataLength; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

Після:
  для (нехай i = 0; i <dataLength; i ++) {
      очікуйте sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }


0

Завдяки RLaaa дав мені уявлення, як вирішити справжню проблему / першопричину попередження. Ну в моєму випадку це був баггі-код MySQL.

За умови, що ви написали Обіцянку з таким кодом всередині:

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

Зауважте, conn.on('error')у коді є слухач. Цей код буквально додає слухача знову і знову, залежить від того, скільки разів ви викликаєте запит. Тим часом if(err) reject(err)робить те саме.

Тому я видалив conn.on('error')слухача і вуаля ... вирішив! Сподіваюся, це вам допоможе.


-4

Помістіть це в перший рядок вашого server.js (або в тому, що містить ваш головний додаток Node.js):

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

і помилка відходить :)


Ви дали мені ідею помістити його в основний файл, і воно спрацювало. Я просто ставив його в неправильне місце. Дякую!
sklimkovitch
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.