ตรวจพบการรั่วไหลของหน่วยความจำ 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

ขอบคุณ. คำแนะนำที่มีประโยชน์มาก
Abdullah Al Farooq

yarn installข้อผิดพลาดนี้เกิดขึ้นกับผมใน ฉันจะวางบรรทัดนี้เพื่อเพิ่มการติดตามสแต็คได้ที่ไหน
วิญญาณโซนิค

คำตอบ:


94

สิ่งนี้อธิบายไว้ในเอกสารคู่มือ event event node

โหนดรุ่นนี้คืออะไร? คุณมีรหัสอื่นอะไร นั่นไม่ใช่พฤติกรรมปกติ

ในระยะสั้นมัน: 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

204

ฉันอยากจะชี้ให้เห็นที่นี่ว่าคำเตือนนั้นมีเหตุผลและมีโอกาสที่ดีที่การแก้ไขที่ถูกต้องไม่เพิ่มขีด จำกัด แต่หาสาเหตุที่คุณเพิ่มผู้ฟังจำนวนมากในเหตุการณ์เดียวกัน เพิ่มขีด จำกัด เฉพาะเมื่อคุณรู้ว่าทำไมผู้ฟังจำนวนมากจึงถูกเพิ่มเข้ามาและมั่นใจว่าเป็นสิ่งที่คุณต้องการจริงๆ

ฉันพบหน้านี้เพราะฉันได้รับคำเตือนนี้และในกรณีของฉันมีข้อผิดพลาดในรหัสบางอย่างที่ฉันใช้ซึ่งทำให้วัตถุทั่วโลกกลายเป็น EventEmitter! ฉันขอแนะนำไม่ให้เพิ่มขีด จำกัด ทั่วโลกเพราะคุณไม่ต้องการให้สิ่งเหล่านี้ไม่มีใครสังเกต


14
+1 ตกลง คำเตือนแสดงสถานะการรั่วไหลที่อาจเกิดขึ้นและการเพิ่ม maxListeners อย่างไม่จำเป็นต้องแก้ไขปัญหา jongleberry.com/understanding-possible-eventemitter-leaks.html
Jeremiah Adams

3
คุณสามารถดีบัก "คำเตือน: ตรวจพบการรั่วไหลของหน่วยความจำที่เป็นไปได้ของหน่วยความจำเพิ่ม 11 ตัวรับฟังข้อผิดพลาดใช้ emitter.setMaxListeners () เพื่อเพิ่มขีด จำกัด " เราควรมองหาอะไร
ฟิล

2
แต่ไม่มีการติดตามสแต็กและไม่มีรหัสใด ๆ พร้อมกับข้อความแสดงข้อผิดพลาดนั้น ฉันได้รับตัวอักษร W และ P ใน "คำเตือน" และ "เป็นไปได้" ดังนั้นฉันคิดว่ามันอาจเป็นข้อผิดพลาดอื่น ฉันต้องการฟังมากกว่าหนึ่งเหตุการณ์ แต่ฉันเคยโทรหา. เพียงครั้งเดียวในทุกกรณีดังนั้นจึงไม่แน่ใจว่าปัญหาคืออะไร
ฟิล

2
@ Phil_1984_ คุณพบวิธีแก้ปัญหาหรือไม่? ถ้าไม่ใช่ดูเหมือนว่าจะใช้งานได้ - stackoverflow.com/questions/38482223/ …
Yoni Jah

3
FYI ลิงก์ของความคิดเห็นแรก (jongleberry.com) ออฟไลน์ นี่คือเวอร์ชันที่เก็บถาวร: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Jeff Ward

76

โดยค่าเริ่มต้นผู้ฟังสูงสุด 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;

BTW รหัสควรอยู่ที่จุดเริ่มต้นของแอป

เพิ่ม: เนื่องจากโหนด 0.11 รหัสนี้ยังทำงานเพื่อเปลี่ยนขีด จำกัด เริ่มต้น:

require('events').EventEmitter.defaultMaxListeners = 0

5
นี่เป็นทางออกเดียวที่ทำงานกับฉันใน Node 5.6.0 ขอบคุณตัน!
Andrew Faulkner

ฉันกำลังใช้ react-native, node version 8. *. * สิ่งนี้ไม่ได้ผลสำหรับฉัน
โทมัสวาเลเดซ

ของฉันต้องการ ('เหตุการณ์') EventEmitter.defaultMaxListeners = Infinity;
Karl Anthony Baluyot

73

คำตอบที่ได้รับการยอมรับจะให้ความหมายเกี่ยวกับวิธีเพิ่มขีด จำกัด แต่ในขณะที่ @voltrevo ชี้ให้เห็นว่าคำเตือนนั้นมีเหตุผลและรหัสของคุณอาจมีข้อผิดพลาด

พิจารณารหัส buggy ต่อไปนี้:

//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
Ganesh Karewad

นี่คือคำตอบที่ถูกต้อง! ฉันคิดอย่างสุจริตว่าคำเตือน maxListener ส่วนใหญ่จะปรากฏขึ้นเพราะรหัสรถ ในกรณีของฉันมันเป็นรหัส mysql ฉันจะพยายามหาคำตอบให้ชัดเจนเพื่อสิ่งนั้น
Adrian

25

แทนที่ด้วย.on() once()การใช้once()ลบฟังเหตุการณ์เมื่อเหตุการณ์ได้รับการจัดการโดยฟังก์ชั่นเดียวกัน

หากวิธีนี้ไม่สามารถแก้ไขได้ให้ติดตั้ง Restler ใหม่ใน package.json "restler" ของคุณ: "git: //github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d"

สิ่งนี้เกี่ยวข้องกับ restler 0.10 ที่ทำงานผิดปกติกับโหนด คุณสามารถดูปัญหาที่ปิดในคอมไพล์ได้ที่นี่: https://github.com/danwrong/restler/issues/112 อย่างไรก็ตาม npm ยังไม่ได้อัปเดตดังนั้นนี่คือสาเหตุที่คุณต้องอ้างถึงหัวคอมไพล์


แก้ไขข้อผิดพลาดนี้ในรหัสของฉันโดยใช้ Puppeterr framework
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 เอกสารและการสร้างหน่วยความจำปล่อยอีซีแอลของเหตุการณ์ที่คล้ายกันพบปัญหานี้เนื่องจากโมดูลnode-apnใช้สำหรับการแจ้งเตือนแบบพุช iOS

วิธีนี้แก้ไขได้:

คุณควรสร้างผู้ให้บริการหนึ่งรายต่อหนึ่งกระบวนการสำหรับคู่ใบรับรอง / คู่คีย์ที่คุณมี คุณไม่จำเป็นต้องสร้างผู้ให้บริการใหม่สำหรับการแจ้งเตือนแต่ละครั้ง หากคุณเพียงส่งการแจ้งเตือนไปยังแอปเดียวก็ไม่จำเป็นต้องมีผู้ให้บริการมากกว่าหนึ่งราย

หากคุณกำลังสร้างอินสแตนซ์ของผู้ให้บริการในแอปอย่างต่อเนื่องให้โทรหา Provider.shutdown () เมื่อคุณทำกับผู้ให้บริการแต่ละรายเพื่อปล่อยทรัพยากรและหน่วยความจำ

ฉันสร้างวัตถุผู้ให้บริการทุกครั้งที่มีการส่งการแจ้งเตือนและคาดว่า gc จะล้างมัน


2

ในกรณีของฉันมันเป็นchild.stderr.pipe(process.stderr)สิ่งที่ถูกเรียกเมื่อฉันเริ่มต้นอินสแตนซ์ของเด็ก 10 คน (หรือมากกว่านั้น) ดังนั้นสิ่งใดก็ตามที่นำไปสู่การแนบตัวจัดการเหตุการณ์กับวัตถุ EventEmitter เดียวกันใน LOOP ทำให้ nodejs โยนข้อผิดพลาดนี้


2

บางครั้งคำเตือนเหล่านี้เกิดขึ้นเมื่อไม่ใช่สิ่งที่เราทำ แต่สิ่งที่เราลืมทำ!

ฉันพบคำเตือนนี้เมื่อฉันติดตั้งแพ็คเกจ dotenv ด้วย npm แต่ถูกขัดจังหวะก่อนที่ฉันจะเข้าไปเพิ่มคำสั่ง ('dotenv'). load () ที่จุดเริ่มต้นของแอป เมื่อฉันกลับไปที่โครงการฉันเริ่มได้รับคำเตือน "ตรวจพบการรั่วไหลของหน่วยความจำ EventEmitter" คำเตือน

ฉันคิดว่าปัญหามาจากสิ่งที่ฉันได้ทำไม่ใช่สิ่งที่ฉันไม่ได้ทำ!

เมื่อฉันค้นพบการกำกับดูแลของฉันและเพิ่มคำสั่ง require คำเตือนการรั่วไหลของหน่วยความจำจะถูกล้าง


2

ฉันชอบที่จะตามล่าและแก้ไขปัญหาแทนการระงับบันทึกเมื่อทำได้ หลังจากสองสามวันของการสังเกตปัญหานี้ในแอปของฉันฉันรู้ว่าฉันกำลังตั้งค่าผู้ฟังreq.socketในมิดเดิลแวร์ Express เพื่อตรวจจับข้อผิดพลาดซ็อกเก็ตไอโอที่ยังคงโผล่ขึ้นมา เมื่อถึงจุดหนึ่งฉันได้เรียนรู้ว่ามันไม่จำเป็น แต่ฉันก็ยังฟังต่อไป ฉันเพิ่งลบพวกเขาและข้อผิดพลาดที่คุณกำลังประสบอยู่หายไป ฉันตรวจสอบว่าเป็นสาเหตุจากการเรียกใช้คำขอไปยังเซิร์ฟเวอร์ของฉันด้วยและไม่มีมิดเดิลแวร์ต่อไปนี้:

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

ฉันมีปัญหาเดียวกัน และปัญหาก็เกิดขึ้นเพราะฉันฟังพอร์ต 8080 จากผู้ฟัง 2 คน

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);
ทราบว่าวัตถุกระบวนการนั้นเป็นสากลสำหรับคำขอทั้งหมดของคุณและเพิ่มผู้ฟัง ทุกครั้งที่มีการร้องขอใหม่เข้ามาจะไม่มีเหตุผลใด ๆ คุณอาจไม่ต้องการพฤติกรรมแบบนั้น
ในกรณีที่คุณต้องการแนบ listener ใหม่สำหรับแต่ละคำขอใหม่คุณควรลบ listeners ก่อนหน้าทั้งหมดที่แนบกับเหตุการณ์เนื่องจากไม่ต้องการใช้อีกต่อไป:
process.removeAllListeners('uncaughtException');


0

การแก้ไขของทีมของเราในการลบเส้นทางรีจิสทรีจาก. npmrc ของเรา เรามีชื่อแทนพา ธ สองรายการในไฟล์ rc และอีกรายการหนึ่งชี้ไปที่อินสแตนซ์ของสิ่งประดิษฐ์ที่เลิกใช้แล้ว

ข้อผิดพลาดไม่เกี่ยวข้องกับรหัสจริงของแอพ แต่ทุกอย่างเกี่ยวข้องกับสภาพแวดล้อมการพัฒนาของเรา


0

ฉันประสบปัญหาเดียวกัน แต่ฉันจัดการกับ async สำเร็จแล้ว
กรุณาตรวจสอบว่ามันช่วย

ให้ dataLength = 25;
ก่อนหน้า:
  สำหรับ (ให้ i = 0; i <dataLength; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

หลัง:
  สำหรับ (ให้ i = 0; i <dataLength; i ++) {
      คอย sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }


0

ขอบคุณRLaaaให้แนวคิดวิธีแก้ปัญหา / สาเหตุที่แท้จริงของคำเตือน ในกรณีของฉันมันเป็นรหัสรถ buggy

ให้คุณเขียนสัญญาพร้อมรหัสภายในเช่นนี้:

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')ผู้ฟังและ voila ... แก้ไข! หวังว่านี่จะช่วยคุณได้


-4

วางสิ่งนี้ไว้ในบรรทัดแรกของ server.js (หรืออะไรก็ตามที่มีแอพ Node.js หลักของคุณ):

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

และข้อผิดพลาดหายไป :)


คุณให้ความคิดแก่ฉันเพื่อนำไปไว้ในไฟล์หลักและใช้งานได้ ฉันแค่วางมันผิดที่ ขอบคุณ!
sklimkovitch
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.