обнаружена утечка памяти в 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 ... Я сделал все, но предупреждение все еще там. :(
Riz

5
Я используюprocess.on('uncaughtException', callback);
Riz

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

11
Я бы не убрал максимальное количество слушателей. Вы не будете получать предупреждения, но вы получите утечки памяти.

15
Как этот ответ получил все эти голоса и был выбран как правильный ответ? хотя это должно работать, но это совершенно неправильно !!
ProllyGeek

205

Здесь я хотел бы указать, что это предупреждение есть по причине, и есть большая вероятность, что правильное исправление не увеличивает предел, а выясняет, почему вы добавляете так много слушателей к одному и тому же событию. Увеличивайте лимит только в том случае, если вы знаете, почему добавляется так много слушателей, и уверены, что это то, чего вы действительно хотите.

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


14
+1. Согласовано. Предупреждение указывает на потенциальную утечку, и бессмысленное увеличение maxListeners не обязательно решит проблему. jongleberry.com/understanding-possible-eventemitter-leaks.html
Иеремия Адамс,

3
Как отладить «Предупреждение: обнаружена возможная утечка памяти в EventEmitter. Добавлено 11 прослушивателей ошибок. Используйте emitter.setMaxListeners () для увеличения лимита». Что мы должны искать?
Фил

2
Но в этом сообщении об ошибке нет ни трассировки стека, ни кода. Я получаю заглавные буквы W и P на «Предупреждение» и «Возможно», поэтому я думаю, что это может быть другая ошибка. Мне нужно прослушать более одного события, но во всех случаях я звоню только один раз, поэтому не знаю, в чем проблема.
Фил

2
@ Phil_1984_ Вы нашли решение? если нет, то это похоже на работу - stackoverflow.com/questions/38482223/…
Йони Джа

3
К вашему сведению, ссылка на первый комментарий (jongleberry.com) недоступна. Вот архивная версия: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Джефф Уорд

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. *. *. Это не сработало для меня.
Томас Валадес

шахта была обязательной ('events'). EventEmitter.defaultMaxListeners = Infinity;
Карл Энтони

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"

Это связано с неправильной работой рестлера 0.10 с узлом. вы можете увидеть проблему закрытой на git здесь: https://github.com/danwrong/restler/issues/112 Однако npm еще не обновил это, поэтому вы должны обратиться к главе git.


это исправить эту ошибку в моем коде с использованием фреймворка Puppeterr
C Алонсо C Ортега


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)

После поиска проблем github, документирования и создания подобных утечек памяти генератора событий эта проблема наблюдалась из -за модуля node-apn, используемого для push-уведомлений iOS.

Это решило это:

Вы должны создать только один провайдер для каждого процесса для каждой пары сертификат / ключ. Вам не нужно создавать нового поставщика для каждого уведомления. Если вы отправляете уведомления только одному приложению, вам не нужно больше одного провайдера.

Если вы постоянно создаете экземпляры поставщика в своем приложении, обязательно вызовите Provider.shutdown (), когда вы закончите с каждым поставщиком, чтобы освободить его ресурсы и память.

Я создавал объект провайдера каждый раз, когда отправлялось уведомление, и ожидал, что gc его очистит.


2

В моем случае это было то, child.stderr.pipe(process.stderr)что вызывали, когда я инициировал 10 (или около того) случаев ребенка. Таким образом, все, что приводит к присоединению обработчика событий к тому же объекту EventEmitter в LOOP, заставляет nodejs выдавать эту ошибку.


2

Иногда эти предупреждения появляются, когда это не то, что мы сделали, а то, что мы забыли сделать!

Я столкнулся с этим предупреждением, когда я установил пакет dotenv с помощью npm, но был прерван, прежде чем я смог добавить оператор require ('dotenv'). Load () в начале моего приложения. Вернувшись в проект, я начал получать предупреждения «Обнаружена утечка памяти в EventEmitter».

Я предположил, что проблема была в том, что я сделал, а не в том, что я не сделал!

Как только я обнаружил свой контроль и добавил оператор require, предупреждение об утечке памяти очистилось.


2

Я предпочитаю выслеживать и исправлять проблемы, а не подавлять журналы всякий раз, когда это возможно. После нескольких дней наблюдения за этой проблемой в моем приложении я понял, что настраиваю слушателей req.socketв промежуточном программном обеспечении Express, чтобы ловить ошибки сокета 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;
До:
  for (пусть i = 0; i <dataLength; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

После:
  for (let i = 0; i <dataLength; i ++) {
      await sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }


0

Спасибо RLaaa за предоставленную мне идею, как решить реальную проблему / первопричину предупреждения. Ну, в моем случае это был глючный код MySQL.

Если вы написали Promise с кодом внутри, вот так:

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;

и ошибка уходит :)


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