可能性のある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);何らかの理由で警告が出ているので使わないでください。
Shwetabh Shekhar 2018

ありがとうございました。非常に役立つ指示。
アブドラアルファルーク2018

このエラーは私に起こりますyarn install。スタックトレースを追加するためにこの行をどこに配置できますか?
Sonic Soul

回答:


94

これはノードのeventEmitterのドキュメントで説明されています

これはどのバージョンのノードですか?他にどんなコードがありますか?これは正常な動作ではありません。

要するに、その: 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 2018

204

ここで指摘したいのは、この警告は理由があるためであり、正しい修正が制限を増やすのではなく、同じイベントに非常に多くのリスナーを追加している理由を理解している可能性が高いです。多くのリスナーが追加されている理由がわかっていて、それが本当に必要なものであると確信している場合にのみ、制限を増やしてください。

この警告が表示されたため、このページを見つけました。私の場合、使用しているコードに、グローバルオブジェクトをEventEmitterに変換するバグがありました。これらのことに気付かれないようにしたくないので、制限をグローバルに増やすことはお勧めしません。


14
+1。同意した。警告は潜在的なリーク状態を示しており、不注意にmaxListenersを増やしても問題は解決しない場合もあります。jongleberry.com/understanding-possible-eventemitter-leaks.html
エレミヤアダムス

3
「警告:可能性のあるEventEmitterメモリーリークが検出されました。11個のエラーリスナーが追加されました。emitter.setMaxListeners()を使用して制限を増やす」をデバッグするにはどうすればよいですか。何を探すべきか?
Phil

2
しかし、スタックトレースはなく、そのエラーメッセージを含むコードはどこにもありません。「警告」と「可能」で大文字のWとPを取得しているため、別のエラーである可能性があります。複数のイベントをリッスンする必要がありますが、すべての場合に.onを呼び出すのは1回だけなので、問題が何であるかはわかりません。
Phil

2
@ Phil_1984_解決策を見つけましたか?-これは動作するようには思えない場合stackoverflow.com/questions/38482223/...
ヨニジャー

3
参考までに、最初のコメントのリンク(jongleberry.com)はオフラインです。:ここにアーカイブされたバージョンであるweb.archive.org/web/20180315203155/http://www.jongleberry.com/...
ジェフ・ワード

76

デフォルトでは、1つのイベントに対して最大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
これは、ノード5.6.0で機能する唯一のソリューションでした。トンありがとう!
Andrew Faulkner 2016年

反応ネイティブのノードバージョン8。*。*を使用しています。これは私にはうまくいきませんでした。
トーマスヴァラデス2018年

私はrequire( 'events')。EventEmitter.defaultMaxListeners = Infinity;でした。
Karl Anthony Baluyot

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()イベントを同じ関数で処理する場合、を使用するとイベントリスナーが削除されます。

これで修正されない場合は、package.json "restler": "git://github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d"にこれを使用してレストラーを再インストールします。

これは、restler 0.10がノードで正しく動作しないことに関係しています。https://github.com/danwrong/restler/issues/112で、gitで問題が解決したことを確認できます ただし、npmはまだこれを更新していないため、gitヘッドを参照する必要があります。


これは、Puppeterrフレームワークを使用する私のコードのこのエラーを修正します
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)

githubの問題、ドキュメントを検索し、同様のイベントエミッターのメモリリークを作成した後、この問題は、iOSプッシュ通知に使用されるnode-apnモジュールが原因で発生しました。

これはそれを解決しました:

所有する証明書/キーペアごとに、プロセスごとにプロバイダーを1つだけ作成する必要があります。通知ごとに新しいプロバイダーを作成する必要はありません。通知を1つのアプリにのみ送信する場合は、複数のプロバイダーを用意する必要はありません。

アプリで常にプロバイダーインスタンスを作成している場合は、各プロバイダーでリソースとメモリを解放するときに、必ずProvider.shutdown()を呼び出してください。

通知が送信されるたびにプロバイダーオブジェクトを作成し、GCがそれをクリアすることを期待していました。


2

私の場合、それはchild.stderr.pipe(process.stderr)私が子の10個程度のインスタンスを開始したときに呼び出されていたものです。したがって、LOOP内の同じEventEmitterオブジェクトにイベントハンドラーをアタッチすることにつながると、nodejsがこのエラーをスローします。


2

これらの警告は、私たちが行ったことではなく、忘れていたときに発生することがあります。

npmでdotenvパッケージをインストールしたときにこの警告が発生しましたが、アプリの先頭にrequire( 'dotenv')。load()ステートメントを追加する前に中断されました。プロジェクトに戻ると、「可能性のあるEventEmitterメモリリークが検出されました」という警告が表示され始めました。

問題は私がやったことではなく、私がやったことではないと思いました!

見落としを見つけてrequireステートメントを追加すると、メモリリークの警告がクリアされました。


2

私は可能な限りログを抑制するのではなく、問題を見つけて修正することを好みます。私のアプリでこの問題を数日間観察した後、req.socketExpressミドルウェアでリスナーを設定し、ポップアップし続けるソケット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

私は同じ問題を抱えていました。2つのリスナーでポート8080をリッスンしていたため、問題が発生しました。

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ファイルには2つのパスエイリアスがあり、1つは廃止されたArtifactoryインスタンスを指しています。

エラーは、私たちのアプリケーションの実際のコードとは何の関係もなく、持っていない、すべて私たちの開発環境で実行するに。


0

同じ問題に直面していましたが、非同期待機で問題なく処理できました。
それが役立つかどうか確認してください。

let dataLength = 25;
前:
  for(let 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;

そしてエラーは消えます:)


あなたはそれをメインファイルに入れるアイデアを私にくれました、そしてそれはうまくいきました。間違った場所に置いただけです。ありがとう!
sklimkovitch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.