web-dev-qa-db-ja.com

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_[〜#〜] and [〜#〜]_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)
_
54
Ifnot

これはnodejsコアのバグであることが判明したため、ここでこの問題について話している: https://github.com/joyent/node/issues/5108

EventEmitter memory leak detectedをスローし、使用可能なメモリ/使用可能なCPU時間を埋めるバグのあるHTTPサーバーのソリューション:

レガシーバージョンv0.8.23に戻します。 (ここからダウンロードしてインストール/コンパイルできます: http://blog.nodejs.org/2013/04/08/node-v0-8-23-legacy/

PDATE 2018:この問題に関するフィードバックはいくつかありますが、問題は何年も経ってなくなっているようです。この応答はnodejsを使用したhttpサーバービルドのリークのみであることに注意してください。他のシナリオにいる場合は、このスレッドで他の応答を確認し、バージョンをダウングレードしないでください(この応答で提案されているように)、時間を無駄にします。

32
Ifnot

私にとっては、イベントループがブロックされているようです。これは、node.jsイベントループでCPU集中型のタスクを実行している場合に発生する可能性があります。 子プロセス を使用して集中的なタスクを実行できます。

以下のメソッドを使用して、node.jsをブロックしているものを確認できます

  1. 各コールの計算時間 を測定します。時間が長い場合はログに記録して、アプリが正しく動作しないことを確認します。
  2. ログスケジュールを設定して、ループをブロックしていることを確認します
    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);
  3. profileを使用します。
  4. ロギングとプロファイリングに this を使用します。 Nodejitsuで使用されるライブラリです。
12
jan salawa

これはまさに私に起こったことです。私にとっては、誤って別のイベントリスナー内にイベントリスナーをネストしました。

コードを見て、たとえば別のイベントリスナーブロック内にイベントリスナーブロックがないことを確認します(意図的に実行している場合を除く)。

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')リスナーはcloseイベントが発生するのを待っています。一度閉じると、別の4番目の終了イベントが実行されます。これは明らかに私が望んでいたものではありません。これは、非ブロッキングであるNode.jsの性質によるものです。コールバック関数によるイベントを「記憶」します。

10
fadleen

ログメッセージをaddListenerに追加するEventEmitterのプロトタイプを作成しようとしましたが、機能しませんでした。

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);
}
6
fardjad

同じオブジェクトの特定のイベントに11回以上登録すると、この警告がスローされます。

頻繁に呼び出している関数で、特定のイベントの「オン」呼び出しがあるかどうかを確認します。これにより、イベントに複数回登録することになります。

これ リンクはこれを理解するのに役立ちました。

5

ノード6以降、node --trace-warningsを使用する必要があります。 https://nodejs.org/api/cli.html#cli_trace_warnings

2
nicojs

単体テストを実行していたときに、これを見ていました。私の単体テストは、呼び出していたコードを繰り返し呼び出していました:

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);
0
pushkin