có thể phát hiện rò rỉ bộ nhớ EventEuctor


231

Tôi đang nhận được cảnh báo sau:

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

Tôi đã viết mã như thế này trong server.js:

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

Làm thế nào để khắc phục điều này?


46
Sử dụng process.on('warning', e => console.warn(e.stack));để gỡ lỗi cảnh báo. Đừng sử dụng process.setMaxListeners(0);như cảnh báo là có vì một số lý do.
Shwetabh Shekhar

Cảm ơn bạn. hướng dẫn rất hữu ích.
Abdullah Al Farooq

lỗi này xảy ra với tôi trên yarn install. Tôi có thể đặt dòng này ở đâu để thêm dấu vết ngăn xếp?
Linh hồn Sonic

Câu trả lời:


94

Điều này được giải thích trong tài liệu về sự kiện nút

Phiên bản nào của Node là đây? Bạn có mã nào khác? Đó không phải là hành vi bình thường.

Nói tóm lại, nó: process.setMaxListeners(0);

Ngoài ra, hãy xem: node.js - request - Làm thế nào để cài đặt emitter.setMaxListener ()?


1
v0.6.11 ... Tôi đã làm mọi thứ, nhưng cảnh báo vẫn còn đó. :(
Riz

5
Tôi đang sử dụngprocess.on('uncaughtException', callback);
Riz

9
process.setMaxListeners(0); // OMG, its so simple... :D
Riz

11
Tôi sẽ không loại bỏ giới hạn người nghe tối đa. Bạn sẽ không nhận được cảnh báo, nhưng bạn sẽ bị rò rỉ bộ nhớ.

15
Làm thế nào mà câu trả lời này nhận được tất cả những phiếu bầu này, và được chọn là câu trả lời đúng? mặc dù nó nên hoạt động, nhưng điều này là hoàn toàn sai !!
ProllyGeek

203

Tôi muốn chỉ ra ở đây rằng cảnh báo đó là có lý do và rất có thể cách khắc phục đúng là không tăng giới hạn nhưng tìm hiểu tại sao bạn lại thêm nhiều người nghe vào cùng một sự kiện. Chỉ tăng giới hạn nếu bạn biết lý do tại sao rất nhiều người nghe được thêm vào và tự tin đó là những gì bạn thực sự muốn.

Tôi tìm thấy trang này bởi vì tôi nhận được cảnh báo này và trong trường hợp của tôi có một lỗi trong một số mã tôi đang sử dụng đang biến đối tượng toàn cầu thành EventEuctor! Tôi chắc chắn khuyên bạn không nên tăng giới hạn trên toàn cầu vì bạn không muốn những điều này không được chú ý.


14
+1. Đã đồng ý. Cảnh báo cho thấy trạng thái rò rỉ tiềm ẩn và việc tăng maxListener sẽ không nhất thiết phải khắc phục sự cố. jongleberry.com/under Hiểu
Jeremiah Adams

3
Làm thế nào bạn có thể gỡ lỗi "Cảnh báo: Có thể phát hiện rò rỉ bộ nhớ EventEuctor. 11 người nghe lỗi đã thêm. Sử dụng emitter.setMaxListener () để tăng giới hạn". Chúng ta nên tìm kiếm cái gì?
Phil

2
Nhưng không có dấu vết ngăn xếp và không có mã ở bất cứ đâu với thông báo lỗi đó. Tôi nhận được vốn W và P trên "Cảnh báo" và "Có thể", vì vậy tôi nghĩ đó có thể là một lỗi khác. Tôi cần nhiều hơn một sự kiện được lắng nghe, nhưng tôi chỉ gọi .on một lần trong mọi trường hợp, vì vậy không chắc vấn đề là gì.
Phil

2
@ Phil_1984_ Bạn đã tìm thấy giải pháp chưa? nếu không điều này có vẻ hiệu quả - stackoverflow.com/questions/38482223/ từ
Yoni Jah

3
FYI, liên kết bình luận đầu tiên (jongleberry.com) đang ngoại tuyến. Đây là phiên bản lưu trữ: web.archive.org/web/20180415203155/http://www.jongleberry.com/ Kẻ
Jeff Ward

76

Theo mặc định, tối đa 10 người nghe có thể được đăng ký cho bất kỳ sự kiện nào.

Nếu đó là mã của bạn, bạn có thể chỉ định maxListener thông qua:

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

Nhưng nếu đó không phải là mã của bạn, bạn có thể sử dụng mẹo để tăng giới hạn mặc định trên toàn cầu:

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

Tất nhiên bạn có thể tắt các giới hạn nhưng hãy cẩn thận:

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

BTW. Mã phải ở ngay đầu ứng dụng.

THÊM: Vì nút 0.11, mã này cũng hoạt động để thay đổi giới hạn mặc định:

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

5
Đây là giải pháp duy nhất hiệu quả với tôi trong Node 5.6.0. Cảm ơn rất nhiều!
Andrew Faulkner

Tôi đang sử dụng Reac -igen, nút phiên bản 8. *. *. Điều này đã không làm việc cho tôi.
Thomas Valadez

của tôi được yêu cầu ('sự kiện'). EventEuctor.defaultMaxListener = Infinity;
Karl Anthony Baluyot

72

Câu trả lời được chấp nhận cung cấp ngữ nghĩa về cách tăng giới hạn, nhưng như @voltrevo chỉ ra rằng cảnh báo là có lý do và mã của bạn có thể có lỗi.

Hãy xem xét mã lỗi sau:

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

Bây giờ hãy quan sát cách thêm chính xác của người nghe:

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

Tìm kiếm các vấn đề tương tự trong mã của bạn trước khi thay đổi maxListener (được giải thích trong các câu trả lời khác)


13
câu trả lời này nên được chấp nhận vì nó cho thấy lý do thực sự đằng sau cảnh báo và cách giải quyết nó, +1
Ganesh Karewad

Đây là câu trả lời chính xác! Tôi thành thật nghĩ rằng cảnh báo maxListener xuất hiện chủ yếu là do một số mã lỗi. Trong trường hợp của tôi, đó là mã mysql. Tôi sẽ cố gắng đưa ra một câu trả lời chỉ để làm rõ hơn cho điều đó.
Adrian

25

Thay thế .on()bằng once(). Việc sử dụng once()loại bỏ trình lắng nghe sự kiện khi sự kiện được xử lý bởi cùng chức năng.

Nếu điều này không khắc phục được, thì hãy cài đặt lại restler với cái này trong gói "restler" của bạn.json: "gitub //danithrong

Điều này phải làm với restler 0.10 hoạt động sai với nút. bạn có thể thấy sự cố đã đóng trên git tại đây: https://github.com/danwrong/restler/issues/112 Tuy nhiên, npm vẫn chưa cập nhật điều này, vì vậy đó là lý do tại sao bạn phải tham khảo đầu git.


khắc phục lỗi này trên mã của tôi bằng khung Puppeterr
C Alonso C Ortega


4

Phiên bản nút: v11.10.1

Thông báo cảnh báo từ stack stack:

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)

Sau khi tìm kiếm các vấn đề về github, tài liệu và tạo rò rỉ bộ nhớ bộ phát sự kiện tương tự, vấn đề này đã được quan sát do mô-đun nút-apn được sử dụng cho thông báo đẩy iOS.

Điều này đã giải quyết nó:

Bạn chỉ nên tạo một Nhà cung cấp cho mỗi quy trình cho mỗi cặp chứng chỉ / khóa mà bạn có. Bạn không cần tạo Nhà cung cấp mới cho mỗi thông báo. Nếu bạn chỉ gửi thông báo đến một ứng dụng thì không cần nhiều Nhà cung cấp.

Nếu bạn liên tục tạo các phiên bản Nhà cung cấp trong ứng dụng của mình, hãy đảm bảo gọi cho Nhà cung cấp.shutdown () khi bạn hoàn thành với mỗi nhà cung cấp để giải phóng tài nguyên và bộ nhớ.

Tôi đã tạo đối tượng nhà cung cấp mỗi lần thông báo được gửi và mong gc xóa nó.


2

Trong trường hợp của tôi, nó đã child.stderr.pipe(process.stderr)được gọi khi tôi bắt đầu 10 (hoặc hơn) trường hợp của đứa trẻ. Vì vậy, bất cứ điều gì, dẫn đến việc đính kèm một trình xử lý sự kiện vào cùng một Đối tượng sự kiện trong LOOP, khiến cho các nútj ném lỗi này.


2

Đôi khi những cảnh báo này xảy ra khi đó không phải là việc chúng tôi đã làm, nhưng là điều chúng tôi quên làm!

Tôi đã gặp cảnh báo này khi tôi cài đặt gói dotenv với npm, nhưng đã bị gián đoạn trước khi tôi đi xung quanh để thêm câu lệnh request ('dotenv'). Load () ở đầu ứng dụng của tôi. Khi tôi trở lại dự án, tôi bắt đầu nhận được cảnh báo "Phát hiện rò rỉ bộ nhớ có thể xảy ra".

Tôi cho rằng vấn đề là từ những gì tôi đã làm, không phải là những gì tôi đã không làm!

Khi tôi phát hiện ra sự giám sát của mình và thêm câu lệnh yêu cầu, cảnh báo rò rỉ bộ nhớ sẽ bị xóa.


2

Tôi thích săn lùng và sửa chữa các vấn đề thay vì đàn áp nhật ký bất cứ khi nào có thể. Sau một vài ngày quan sát vấn đề này trong ứng dụng của mình, tôi nhận ra rằng tôi đang đặt trình nghe trên req.socketphần mềm trung gian Express để bắt lỗi io của ổ cắm liên tục xuất hiện. Tại một số điểm, tôi đã học được rằng điều đó là không cần thiết, nhưng dù sao tôi vẫn giữ người nghe. Tôi chỉ cần loại bỏ chúng và lỗi bạn đang gặp phải đã biến mất. Tôi đã xác minh đó là nguyên nhân bằng cách chạy các yêu cầu đến máy chủ của mình có và không có phần mềm trung gian sau:

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

Loại bỏ phần mềm trung gian đó đã dừng cảnh báo bạn đang thấy. Tôi sẽ xem xét mã của bạn và cố gắng tìm bất cứ nơi nào bạn có thể đang thiết lập trình nghe mà bạn không cần.


1

Tôi đã có cùng một vấn đề. và vấn đề đã được gây ra bởi vì tôi đang nghe cổng 8080, trên 2 người nghe.

setMaxListeners() hoạt động tốt, nhưng tôi sẽ không đề nghị nó.

cách chính xác là, kiểm tra mã của bạn để biết thêm người nghe, loại bỏ người nghe hoặc thay đổi số cổng mà bạn đang nghe, điều này đã khắc phục vấn đề của tôi.


1

Tôi đã có điều này cho đến hôm nay khi tôi bắt đầu grunt watch. Cuối cùng giải quyết bằng

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

Tin nhắn phiền phức đã biến mất.


1

Bạn cần xóa tất cả người nghe trước khi tạo người nghe mới bằng cách sử dụng:

Máy khách / Máy chủ

socket.removeAllListeners(); 

Giả sử ổ cắm là ổ cắm máy khách của bạn / hoặc ổ cắm máy chủ được tạo.

Bạn cũng có thể đăng ký từ những người nghe sự kiện cụ thể như ví dụ loại bỏ connectngười nghe như thế này:

this.socket.removeAllListeners("connect");

0

Bạn nói rằng bạn đang sử dụng process.on('uncaughtException', callback);
Bạn đang thực hiện tuyên bố này ở đâu? Có phải trong vòng gọi lại được chuyển đến http.createServer?
Nếu có, bản sao khác nhau của cùng một cuộc gọi lại sẽ được đính kèm vào sự kiện unsceptionException theo mỗi yêu cầu mới, bởi vì yêu cầu function (req, res) { ... }được thực hiện mỗi khi có yêu cầu mới xuất hiện và câu lệnh cũng sẽ process.on('uncaughtException', callback);
lưu ý rằng đối tượng xử lý là toàn cầu đối với tất cả các yêu cầu của bạn và thêm người nghe đến sự kiện của nó mỗi khi có yêu cầu mới sẽ không có ý nghĩa gì. Bạn có thể không muốn loại hành vi như vậy.
Trong trường hợp bạn muốn đính kèm một trình nghe mới cho mỗi yêu cầu mới, bạn nên xóa tất cả các trình nghe trước đó được đính kèm vào sự kiện vì chúng không còn được yêu cầu sử dụng:
process.removeAllListeners('uncaughtException');


0

Bản sửa lỗi của nhóm chúng tôi đã xóa đường dẫn đăng ký khỏi .npmrc của chúng tôi. Chúng tôi có hai bí danh đường dẫn trong tệp RC và một chỉ ra một thể hiện Artifactory đã bị phản đối.

Lỗi không liên quan gì đến mã thực tế của Ứng dụng nhưng mọi thứ liên quan đến môi trường phát triển của chúng tôi.


0

Tôi đã phải đối mặt với cùng một vấn đề, nhưng tôi đã xử lý thành công với sự chờ đợi không đồng bộ.
Vui lòng kiểm tra nếu nó giúp.

để dữ liệuLạng = 25;
Trước:
  for (let i = 0; i <dataL wavel; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

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


0

Cảm ơn RLaaa đã cho tôi một ý tưởng làm thế nào để giải quyết vấn đề thực sự / nguyên nhân gốc rễ của cảnh báo. Trong trường hợp của tôi, đó là mã lỗi của MySQL.

Cung cấp cho bạn đã viết một Promise với mã bên trong như thế này:

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

Lưu ý có một conn.on('error')người nghe trong mã. Mã đó theo nghĩa đen thêm người nghe lặp đi lặp lại tùy thuộc vào số lần bạn gọi truy vấn. Trong khi đó if(err) reject(err)làm điều tương tự.

Vì vậy, tôi loại bỏ conn.on('error')người nghe và voila ... giải quyết! Hy vọng điều này sẽ giúp bạn.


-4

Đặt phần này vào dòng đầu tiên của server.js của bạn (hoặc bất cứ thứ gì có chứa ứng dụng Node.js chính của bạn):

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

và lỗi sẽ biến mất :)


Bạn đã cho tôi một ý tưởng để đặt nó trong một tập tin chính, và nó đã hoạt động. Tôi chỉ đặt nó ở một vị trí sai. Cảm ơn!
sklimkovitch
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.