kemungkinan kebocoran memori EventEmitter terdeteksi

231

Saya mendapatkan peringatan berikut:

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

Saya menulis kode seperti ini di server.js:

http.createServer(
    function (req, res) { ... }).listen(3013);

Bagaimana cara memperbaikinya?

Riz
sumber
46
Gunakan process.on('warning', e => console.warn(e.stack));untuk men-debug peringatan. Jangan gunakan process.setMaxListeners(0);karena peringatan ada karena alasan tertentu.
Shwetabh Shekhar
Terima kasih. instruksi yang sangat berguna.
Abdullah Al Farooq
kesalahan ini terjadi pada saya pada yarn install. di mana saya bisa meletakkan baris ini untuk menambahkan jejak stack?
Sonic Soul

Jawaban:

94

Ini dijelaskan dalam dokumentasi eventEmitter node

Node versi apa ini? Apa kode lain yang Anda miliki? Itu bukan perilaku normal.

Singkatnya, ini: process.setMaxListeners(0);

Juga lihat: node.js - request - Bagaimana "emitter.setMaxListeners ()"?

Corey Richardson
sumber
1
v0.6.11 ... Saya melakukan segalanya, tetapi peringatan masih ada. :(
Riz
5
Saya menggunakanprocess.on('uncaughtException', callback);
Riz
9
process.setMaxListeners(0); // OMG, its so simple... :D
Riz
11
Saya tidak akan menghapus batas pendengar maksimal. Anda tidak akan mendapatkan peringatan, tetapi Anda akan mendapatkan kebocoran memori.
15
Bagaimana jawaban ini mendapatkan semua suara ini, dan dipilih sebagai jawaban yang benar? Meskipun itu seharusnya bekerja, tetapi ini sepenuhnya salah !!
ProllyGeek
204

Saya ingin menunjukkan di sini bahwa peringatan itu ada karena suatu alasan dan ada peluang bagus perbaikan yang tepat tidak meningkatkan batas tetapi mencari tahu mengapa Anda menambahkan begitu banyak pendengar ke acara yang sama. Tingkatkan batas jika Anda tahu mengapa begitu banyak pendengar ditambahkan dan yakin itulah yang Anda inginkan.

Saya menemukan halaman ini karena saya mendapat peringatan ini dan dalam kasus saya ada bug dalam beberapa kode yang saya gunakan yang mengubah objek global menjadi EventEmitter! Saya tentu saja menyarankan untuk tidak meningkatkan batas secara global karena Anda tidak ingin hal-hal ini tidak diperhatikan.

voltrevo
sumber
14
+1. Sepakat. Peringatan menunjukkan potensi kondisi kebocoran dan peningkatan maxListeners yang tidak dipikirkan tidak akan memperbaiki masalah. jongleberry.com/understanding-possible-eventemitter-leaks.html
Jeremiah Adams
3
Bagaimana Anda dapat men-debug "Peringatan: Kemungkinan Kebocoran memori EventEmitter terdeteksi. 11 pendengar kesalahan ditambahkan. Gunakan emitter.setMaxListeners () untuk menambah batas". Apa yang harus kita cari?
Phil
2
Tetapi tidak ada jejak stack dan tidak ada kode di mana pun dengan pesan kesalahan itu. Saya mendapatkan modal W dan P pada "Peringatan" dan "Kemungkinan", jadi saya pikir itu mungkin kesalahan yang berbeda. Saya perlu lebih dari satu acara untuk mendengarkan, tetapi saya hanya pernah menelepon. Sekali saja dalam semua kasus, jadi tidak yakin apa masalahnya.
Phil
2
@ Phil_1984_ Sudahkah Anda menemukan solusi? jika tidak ini tampaknya berhasil - stackoverflow.com/questions/38482223/…
Yoni Jah
3
FYI, tautan komentar pertama (jongleberry.com) sedang luring. Ini adalah versi yang diarsipkan: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Jeff Ward
76

Secara default, maksimal 10 pendengar dapat didaftarkan untuk acara apa pun.

Jika ini kode Anda, Anda dapat menentukan maxListeners melalui:

const emitter = new EventEmitter()
emitter.setMaxListeners(100)
// or 0 to turn off the limit
emitter.setMaxListeners(0)

Tetapi jika itu bukan kode Anda, Anda dapat menggunakan trik ini untuk meningkatkan batas default secara global:

require('events').EventEmitter.prototype._maxListeners = 100;

Tentu saja Anda dapat mematikan batas tetapi hati-hati:

// turn off limits by default (BE CAREFUL)
require('events').EventEmitter.prototype._maxListeners = 0;

BTW. Kode harus berada di awal aplikasi.

TAMBAH: Karena simpul 0.11 kode ini juga berfungsi untuk mengubah batas default:

require('events').EventEmitter.defaultMaxListeners = 0
zag2art
sumber
5
Ini adalah satu-satunya solusi yang bekerja untuk saya di Node 5.6.0. Terima kasih banyak!
Andrew Faulkner
Saya menggunakan reaksi-asli, simpul versi 8. *. *. Ini tidak berhasil untuk saya.
Thomas Valadez
punyaku memerlukan ('acara'). EventEmitter.defaultMaxListeners = Infinity;
Karl Anthony Baluyot
73

Jawaban yang diterima memberikan semantik tentang cara meningkatkan batas, tetapi seperti @voltrevo menunjukkan bahwa peringatan ada karena suatu alasan dan kode Anda mungkin memiliki bug.

Pertimbangkan kode kereta berikut:

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

Sekarang perhatikan cara menambahkan pendengar yang benar:

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

Cari masalah serupa dalam kode Anda sebelum mengubah maxListeners (yang dijelaskan dalam jawaban lain)

Rayee Roded
sumber
13
jawaban ini harus diterima karena menunjukkan alasan sebenarnya di balik peringatan dan bagaimana menyelesaikannya, +1
Ganesh Karewad
Ini JAWABAN YANG BENAR! Jujur saya berpikir bahwa peringatan maxListener muncul sebagian besar karena beberapa kode kereta. Dalam kasus saya itu adalah kode mysql. Saya akan mencoba memberikan jawaban hanya untuk memberikan kejelasan untuk itu.
Adrian
25

Ganti .on()dengan once(). Menggunakan once()menghilangkan pendengar acara ketika acara ditangani oleh fungsi yang sama.

Jika ini tidak memperbaikinya, instal ulang restler dengan ini di package.json Anda "restler": "git: //github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d"

Ini ada hubungannya dengan restler 0.10 nakal dengan node. Anda dapat melihat masalah ditutup pada git di sini: https://github.com/danwrong/restler/issues/112 Namun, npm belum memperbarui ini, jadi itu sebabnya Anda harus merujuk ke kepala git.

Davis Dulin
sumber
perbaiki kesalahan ini pada kode saya menggunakan kerangka Puppeterr
C Alonso C Ortega
5

Saya mendapatkan peringatan ini juga ketika menginstal aglio di mac osx saya.

Saya menggunakan cmd memperbaikinya.

sudo npm install -g npm@next

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

Legolas Bloom
sumber
4

Versi Node: v11.10.1

Pesan peringatan dari tumpukan jejak:

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)

Setelah mencari masalah github, dokumentasi dan membuat kebocoran memori emitor peristiwa serupa, masalah ini diamati karena modul node-apn digunakan untuk notifikasi push iOS.

Ini menyelesaikannya:

Anda hanya harus membuat satu Penyedia per proses untuk setiap pasangan sertifikat / kunci yang Anda miliki. Anda tidak perlu membuat Penyedia baru untuk setiap notifikasi. Jika Anda hanya mengirim pemberitahuan ke satu aplikasi maka tidak perlu lebih dari satu Penyedia.

Jika Anda terus-menerus membuat instance Penyedia di aplikasi Anda, pastikan untuk memanggil Provider.shutdown () ketika Anda selesai dengan masing-masing penyedia untuk melepaskan sumber daya dan memori.

Saya membuat objek penyedia setiap kali pemberitahuan dikirim dan mengharapkan gc untuk menghapusnya.

Sandeep PC
sumber
2

Dalam kasus saya, itu child.stderr.pipe(process.stderr)yang dipanggil ketika saya memulai 10 (atau lebih) contoh anak. Jadi apa pun, yang mengarah untuk melampirkan pengendali acara ke Objek EventEmitter yang sama di LOOP, menyebabkan nodejs untuk melempar kesalahan ini.

Vikas Gautam
sumber
2

Terkadang peringatan ini muncul ketika itu bukan sesuatu yang kami lakukan, tetapi sesuatu yang kami lupa lakukan!

Saya mengalami peringatan ini ketika saya menginstal paket dotenv dengan npm, tetapi terputus sebelum saya menambahkan pernyataan need ('dotenv'). Load () di awal aplikasi saya. Ketika saya kembali ke proyek, saya mulai mendapatkan peringatan "Kemungkinan kebocoran memori EventEmitter terdeteksi".

Saya berasumsi masalahnya adalah dari sesuatu yang telah saya lakukan, bukan sesuatu yang belum saya lakukan!

Setelah saya menemukan pengawasan saya dan menambahkan pernyataan yang diperlukan, peringatan kebocoran memori dihapus.

Memotivasi
sumber
2

Saya lebih memilih untuk memburu dan memperbaiki masalah daripada menekan log bila memungkinkan. Setelah beberapa hari mengamati masalah ini di aplikasi saya, saya menyadari bahwa saya sedang mengatur pendengar di req.socketdalam middleware Express untuk menangkap kesalahan socket io yang terus bermunculan. Pada titik tertentu, saya belajar bahwa itu tidak perlu, tetapi saya tetap menjaga pendengar di sekitar. Saya baru saja menghapusnya dan kesalahan yang Anda alami hilang. Saya memverifikasi penyebabnya dengan menjalankan permintaan ke server saya dengan dan tanpa middleware berikut:

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

Menghapus middleware itu menghentikan peringatan yang Anda lihat. Saya akan melihat-lihat kode Anda dan mencoba mencari di mana saja Anda mungkin mengatur pendengar yang tidak Anda butuhkan.

lwdthe1
sumber
1

Saya mengalami masalah yang sama. dan masalahnya disebabkan karena saya mendengarkan port 8080, pada 2 pendengar.

setMaxListeners() berfungsi dengan baik, tetapi saya tidak akan merekomendasikan hal ini.

cara yang benar adalah dengan, memeriksa kode Anda untuk pendengar tambahan, menghapus pendengar atau mengubah nomor port yang Anda dengarkan, ini memperbaiki masalah saya.

Noman Abid
sumber
1

Saya mengalami ini sampai hari ini ketika saya mulai grunt watch. Akhirnya diselesaikan oleh

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

Pesan yang menjengkelkan hilang.

Ariful Haque
sumber
1

Anda harus menghapus semua pendengar sebelum membuat yang baru menggunakan:

Server klien

socket.removeAllListeners(); 

Soket asumsi adalah soket klien Anda / atau soket server yang dibuat.

Anda juga dapat berlangganan dari pendengar acara tertentu seperti misalnya menghapus connectpendengar seperti ini:

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

Anda bilang Anda menggunakan process.on('uncaughtException', callback);
Di mana Anda menjalankan pernyataan ini? Apakah dalam callback diteruskan ke http.createServer?
Jika ya, salinan berbeda dari panggilan balik yang sama akan dilampirkan ke acara uncaughtException atas setiap permintaan baru, karena function (req, res) { ... }dieksekusi setiap kali permintaan baru masuk dan begitu juga pernyataan process.on('uncaughtException', callback);
Perhatikan bahwa objek proses global untuk semua permintaan Anda dan menambahkan pendengar ke acara setiap kali permintaan baru masuk tidak akan masuk akal. Anda mungkin tidak menginginkan perilaku seperti itu.
Jika Anda ingin melampirkan pendengar baru untuk setiap permintaan baru, Anda harus menghapus semua pendengar sebelumnya yang dilampirkan ke acara tersebut karena mereka tidak lagi diharuskan menggunakan:
process.removeAllListeners('uncaughtException');

Monish Chhadwa
sumber
0

Perbaikan tim kami untuk ini adalah menghapus jalur registri dari .npmrc kami. Kami memiliki dua alias path dalam file rc, dan satu menunjuk ke instance Artifactory yang telah usang.

Kesalahan itu tidak ada hubungannya dengan kode aktual Aplikasi kami, tetapi semuanya terkait dengan lingkungan pengembangan kami.

RossO
sumber
0

Saya menghadapi masalah yang sama, tetapi saya telah berhasil menangani async menunggu.
Silakan periksa apakah itu membantu.

biarkan dataLength = 25;
Sebelum:
  untuk (mis. I = 0; i <dataLength; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

After:
  for (let i = 0; i <dataLength; i ++) {
      tunggu sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

Vivek Mehta
sumber
0

Terima kasih kepada RLaaa untuk memberi saya ide bagaimana menyelesaikan masalah sebenarnya / akar penyebab peringatan. Nah dalam kasus saya itu adalah kode kereta MySQL.

Memberikan Anda menulis Janji dengan kode di dalamnya seperti ini:

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

Perhatikan ada conn.on('error')pendengar dalam kodenya. Kode yang secara harfiah menambahkan pendengar berulang tergantung pada berapa kali Anda memanggil kueri. Sementara itu if(err) reject(err)melakukan hal yang sama.

Jadi saya menghapus conn.on('error')pendengar dan voila ... terpecahkan! Semoga ini bisa membantu Anda.

Adrian
sumber
-4

Letakkan ini di baris pertama server.js Anda (atau apa pun yang berisi aplikasi Node.js utama Anda):

require('events').EventEmitter.prototype._maxListeners = 0;

dan kesalahan hilang :)

Sebastian
sumber
Anda memberi saya ide untuk meletakkannya di file utama, dan itu berhasil. Saya hanya meletakkannya di tempat yang salah. Terima kasih!
sklimkovitch