NodeJS: как отладить обнаруженную утечку памяти EventEmitter, добавлено 11 слушателей " - программирование
Подтвердить что ты не робот

NodeJS: как отладить обнаруженную утечку памяти EventEmitter, добавлено 11 слушателей "

Как отлаживать приложение, которое выдает эту ошибку:

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

Я не мог найти предполагаемый объект утечки для увеличения предела слушателя на .setMaxListeners(0);

РЕШЕНИЕ (от fardjad и jan salawa)

С помощью поиска jan salawa я нашел рабочую библиотеку (longjohn) для увеличения подробностей стека. С ответом fardjad я обнаружил, что нам нужно прототип EventEmitter.addListener И EventEmitter.on.

С помощью решения я мог бы получить эту новую трассировку:

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

Ответ 1

Оказывается, это ошибка в ядре nodejs, мы говорим об этой проблеме здесь: https://github.com/joyent/node/issues/5108

Решение для прослушиваемых http-серверов, которое EventEmitter memory leak detected и заполняет доступную память/доступное время процессора:

Вернитесь к устаревшей версии v0.8.23. (Вы можете скачать и установить/скомпилировать его здесь: http://blog.nodejs.org/2013/04/08/node-v0-8-23-legacy/)

ОБНОВЛЕНИЕ 2018: я вижу несколько отзывов на эту тему, в то время как проблема, похоже, исчезла с годами. Обратите внимание, что этот ответ предназначен только для протекающей сборки http-сервера с nodejs. Если вы находитесь в другом сценарии, просмотрите ответы других в этой теме и не понижайте версию (как предложено в этом ответе), вы потеряете свое время.

Ответ 2

Мне кажется, что ваш цикл событий заблокирован. Это может произойти, если вы выполняете интенсивные задачи cpu в цикле событий node.js. Вы можете использовать дочерний процесс для выполнения интенсивной задачи.

Вы можете проверить, что блокирует node.js, используя следующие методы:

  • Измерьте время вычисления для каждого вызова. Log, если время велико, поэтому вы знаете, что приложение плохо себя ведет.
  • Настройте расписание журнала, чтобы вы знали, когда что-то блокирует цикл
    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);
  • Используйте профиль.
  • Используйте этот для ведения журнала и профилирования. Эта библиотека используется в Nodejitsu.

Ответ 3

Именно это и произошло со мной. Для меня я случайно ввел прослушиватель событий в другой прослушиватель событий.

Посмотрите на свой код и убедитесь, что у вас нет блока прослушивателя событий WITHIN другого блока прослушивателя событий, например (если вы не делаете это специально):

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

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

   });

В неправильном примере выше, слушатель socket.on('close') должен быть ВНЕШНИМ блоком socket.on('data').

В моем случае, когда я получил 5 потоков данных, слушатель socket.on('close') ждет события закрытия. Когда я закрою один раз, будет выполнено еще одно четвертое событие закрытия. Это явно не то, что я хотел. Это связано с характером Node.js, который не блокирует. Он запоминает события из-за функции обратного вызова.

Ответ 4

Я попытался прототипировать EventEmitter для добавления сообщений журнала в addListener, но я не мог заставить его работать

Чтобы зацепить addListener, вы можете сделать что-то вроде этого:

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

Ответ 5

Это предупреждение будет выдано, если вы зарегистрируетесь для определенного события одного и того же объекта более 11 раз.

Проверьте, есть ли у вас 'on' вызов для конкретного события в функции, которую вы часто вызываете, это приводит к регистрации события несколько раз.

Эта ссылка помогла мне понять это.

Ответ 7

Я видел это, когда проводил свои юнит-тесты. Мои модульные тесты неоднократно вызывали код, который вызывал:

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

Мне пришлось использовать внедрение зависимостей, чтобы внедрить поддельный объект процесса, что решило проблему.

main.js:

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

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

Мои юнит-тесты сделали бы:

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

Ответ 8

Я столкнулся с той же проблемой при тестировании Реагировать с помощью мокко и фермента.

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

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

Я изменил свой тестовый код, добавив строку rendered.unmount(). Это устранило проблему для меня.

describe('<CircleArc />', () => {

    it('renders', function () {
        const rendered = mount(<CircleArc />);
        assert.ok(rendered.find('path'));
        rendered.unmount();
    });
}