web-dev-qa-db-ja.com

EventEmitterのメモリの可能性が検出された

次のような警告が表示されます。

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

これを修正するには?

171
Dev

これはマニュアルで説明されています: http://nodejs.org/docs/latest/api/events.html#events_emitter_setmaxlisteners_n

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

一言で言えば:process.setMaxListeners(0);

また、参照してください: node.js - request - How to“ emit.setMaxListeners()”?

81

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

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

154
voltrevo

デフォルトでは、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
73
zag2art

受け入れられた答えは制限を増やす方法に関する意味論を提供します、しかし@ 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');
}

MaxListenerを変更する前に、コード内で同様の問題を検索してください(他の回答で説明されています)。

46
RLaaa

.on()once()に置き換えます。 once()を使用すると、イベントが同じ関数によって処理されるときにイベントリスナーが削除されます。出典: http://nodeguide.com/beginner.html#using-eventemitters

これで問題が解決しない場合は、package.jsonの「restler」にこれを使用してrestlerを再インストールしてください。

これはrestler 0.10がnodeと誤動作していることと関係があります。 gitで問題が解決したことがわかります。 https://github.com/danwrong/restler/issues/112 ただし、npmはまだ更新されていませんこれが、Gitのヘッドを参照する必要がある理由です。

18
Davis Dulin

私のMac OSXにaglioをインストールすると、私もこの警告を受けます。

Cmdを使用して修正します。

Sudo npm install -g npm@next

https://github.com/npm/npm/issues/13806

3
Legolas Bloom

私はgrunt watchを始めたときに今日までこれをしていました。最後に解決した

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

迷惑なメッセージは消えました。

1
Ariful Haque

可能な限りログを抑制するのではなく、問題を探し出して問題を解決することをお勧めします。私のアプリでこの問題を観察して数日後、私は、ポップアップミドルウェアのソケットioエラーを捕捉するために、Expressミドルウェアのreq.socketにリスナーを設定していたことに気づきました。ある時点で、私はそれが必要ではないことを知りました、しかし私はとにかく周りのリスナーを続けました。削除したばかりで、発生していたエラーはなくなりました。次のミドルウェアを使用して、または使用せずにサーバーにリクエストを実行したことが原因であることを確認しました。

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
lwdthe1

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

1
Vikas Gautam

時々これらの警告はそれが私たちがしたことではなく、私たちがするのを忘れたことではないときに発生します!

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

私は、問題は自分がしたことではなく、自分がしたことがないことにあると思いました。

私が自分の見落としを発見し、requireステートメントを追加すると、メモリリークの警告はクリアされました。

1
Motate

私は同じ問題を抱えていました。私は2人のリスナーで、ポート8080を聞いていたので、そして問題は引き起こされました。

setMaxListeners()は問題なく動作しますが、お勧めしません。

正しい方法は、コードに余分なリスナーがないか確認し、リスナーを削除するか、リスニングしているポート番号を変更することです。これで私の問題は解決しました。

0
Noman Abid

これに対する私たちのチームの修正は、私たちの.npmrcからレジストリパスを削除することでした。 rcファイルには2つのパスエイリアスがあり、1つは廃止予定のArtifactoryインスタンスを指していました。

このエラーは、私たちのアプリケーションの実際のコードとは関係ありませんが、すべて私たちの開発環境と関係があります。

0
RossO

ノードバージョン: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インスタンスを作成している場合は、各プロバイダーの使用が終わったら必ずProvider.shutdown()を呼び出して、リソースとメモリを解放してください。

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

0
Sandeep PC

あなたはprocess.on('uncaughtException', callback);を使っていると言いました
あなたはこの声明をどこで執行していますか?それはhttp.createServerに渡されるコールバックの中にありますか?
yesの場合、新しいリクエストがあるたびに同じコールバックの異なるコピーがncaughtExceptionイベントに添付されます。これは、新しいリクエストが来るたびにfunction (req, res) { ... }が実行され、ステートメントprocess.on('uncaughtException', callback);が実行されるためです
プロセスオブジェクトはすべてのリクエストに対してグローバルであり、新しいリクエストが来るたびにそのイベントにリスナーを追加しても何も行われません。センス。あなたはそのような行動を望んでいないかもしれません。
新しいリクエストごとに新しいリスナーをアタッチする場合は、イベントを使用してアタッチした以前のリスナーをすべて削除する必要があります。
process.removeAllListeners('uncaughtException');

0
Monish Chhadwa

次のものを使用して新しいリスナを作成する前に、すべてのリスナをクリアする必要があります。

クライアント/サーバー

socket.removeAllListeners(); 

ソケットがあなたのクライアントソケットまたは作成されたサーバーソケットであると仮定します。

また、例えば次のようにconnectリスナーを削除するなど、特定のイベントリスナーから購読することもできます。

this.socket.removeAllListeners("connect");
0
ProllyGeek