检测到可能的EventEmitter内存泄漏

时间:2012-03-19 10:20:59

标签: node.js memory-leaks 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);

如何解决这个问题?

21 个答案:

答案 0 :(得分:153)

我想在此指出,警告是有原因的,并且正确的修复很有可能增加限制,但要弄清楚为什么你&# 39;重新添加如此多的监听器到同一事件。只有当你知道为什么要添加这么多听众并确信你真正想要它时,才能增加限制。

我找到了这个页面,因为我收到了这个警告,在我的情况下,我使用的一些代码中存在一个错误,即将全局对象转换为EventEmitter!我当然建议不要在全球范围内提高限额,因为你不希望这些事情被忽视。

答案 1 :(得分:81)

手册中解释了这一点:http://nodejs.org/docs/latest/api/events.html#events_emitter_setmaxlisteners_n

这是什么版本的Node?您还有其他什么代码?这不是正常行为。

简而言之,它是:process.setMaxListeners(0);

另见:node.js - request - How to “emitter.setMaxListeners()”?

答案 2 :(得分:72)

默认情况下,任何单个事件最多可以注册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;

顺便说一句。代码应该在应用程序的最开头。

ADD:从节点0.11开始,此代码也可以更改默认限制:

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

答案 3 :(得分:46)

接受的答案提供了关于如何增加限制的语义,但是@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之前在代码中搜索类似的问题(在其他答案中有解释)

答案 4 :(得分:18)

.on()替换为once()。当事件由同一函数处理时,使用once()删除事件侦听器。资料来源:http://nodeguide.com/beginner.html#using-eventemitters

如果这不能解决问题,请在package.json中重新安装restler “restler”:“git://github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d”

这与restler 0.10与节点行为不当有关。您可以在此处看到关闭git的问题:https://github.com/danwrong/restler/issues/112 但是,npm尚未对此进行更新,因此您必须参考git head。

答案 5 :(得分:3)

我在mac osx上安装aglio时也会收到此警告。

我使用cmd修复它。

sudo npm install -g npm@next

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

答案 6 :(得分:1)

直到今天我才开始grunt watch。最后通过

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

烦人的消息消失了。

答案 7 :(得分:1)

感谢RLaaa给了我一个解决实际警告问题/根本原因的想法。好吧,在我看来,这是MySQL错误代码。

假设您编写的Promise的代码如下:

execSync()

请注意,代码中有一个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)侦听器,瞧……解决了! 希望对您有帮助。

答案 8 :(得分:1)

我更愿意追捕并修复问题,而不是尽可能地抑制日志。在我的应用程序中观察了这个问题几天之后,我意识到我在Express中间件的req.socket上设置了监听器来捕获不断弹出的socket 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();
    }

删除该中间件会停止您看到的警告。我会环顾你的代码并尝试找到你可能正在设置你不需要的听众的任何地方。

答案 9 :(得分:1)

有时这些警告会发生在我们没有做过的事情上,而是我们忘记做的事情!

当我用npm安装dotenv软件包时遇到了这个警告,但在我开始在我的应用程序开头添加require('dotenv')。load()语句之前被中断了。当我回到项目时,我开始收到“检测到可能的EventEmitter内存泄漏”警告。

我认为问题来自于我所做的事情,而不是我没有做过的事情!

一旦我发现我的疏忽并添加了require语句,就会清除内存泄漏警告。

答案 10 :(得分:1)

在我的情况下,当我启动孩子的10个(左右)实例时,它被调用child.stderr.pipe(process.stderr)。因此,导致将事件处理程序附加到LOOP中的同一EventEmitter对象的任何事情都会导致nodejs抛出此错误。

答案 11 :(得分:0)

我们团队的解决方法是从.npmrc中删除注册表路径。我们在rc文件中有两个路径别名,一个指向已弃用的Artifactory实例。

该错误与我们的应用程序的实际代码无关,但所有与我们的开发环境无关。

答案 12 :(得分:0)

我遇到了同样的问题。这个问题是因为我正在听两个听众的8080端口。

setMaxListeners()工作正常,但我不推荐它。

正确的方法是,检查代码以获取额外的侦听器,删除侦听器或更改您正在侦听的端口号,这解决了我的问题。

答案 13 :(得分:0)

你说你正在使用process.on('uncaughtException', callback);
你在哪里执行这个陈述?是否在传递给http.createServer的回调中?如果是,则在每个新请求时,相同回调的不同副本将附加到 uncaughtException 事件,因为{{1每次新请求进入时都会执行,语句function (req, res) { ... }也是如此。请注意,进程对象是所有请求的全局,并且每次都将监听器添加到其事件中新请求进来将没有任何意义。您可能不希望出现这种行为。
如果您想为每个新请求附加一个新的侦听器,您应该删除附加到该事件的所有先前侦听器,因为它们不再需要使用:
process.on('uncaughtException', callback);

答案 14 :(得分:0)

使用以下方法创建新的监听器之前,您需要清除所有监听器:

客户端/服务器

socket.removeAllListeners(); 

假设套接字是您的客户端套接字/或创建的服务器套接字。

您还可以订阅特定的事件侦听器,例如像这样删除connect侦听器:

this.socket.removeAllListeners("connect");

答案 15 :(得分:0)

节点版本: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 模块,发现了此问题。

此问题已解决:

  

您应仅为每个进程创建一个提供程序   您拥有的证书/密钥对。您不需要创建新的   每个通知的提供者。如果您仅发送通知   到一个应用程序,则无需多个提供商。

     

如果您在应用程序中不断创建Provider实例,请   完成每个提供者的操作后,一定要调用Provider.shutdown()   释放其资源和内存。

每次发送通知时,我都在创建提供程序对象,并期望gc清除它。

答案 16 :(得分:0)

我遇到了同样的问题,但是我已经成功地通过异步等待进行了处理。
请检查是否有帮助。

let dataLength = 25;
之前
for(让i = 0; i sftp.get(remotePath,fs.createWriteStream(xyzProject/${data[i].name}));
}

之后:
for(让i = 0; i 等待sftp.get(remotePath,fs.createWriteStream(xyzProject/${data[i].name}));
}

答案 17 :(得分:0)

正如其他人所指出的,增加限制并不是最好的答案。我遇到了同样的问题,但在我的代码中我没有使用任何事件侦听器。当我仔细查看代码时,我有时会创建很多承诺。每个承诺都有一些抓取提供的 URL 的代码(使用一些第三方库)。如果您正在做类似的事情,那么这可能是原因。

请参阅此线程以了解如何防止这种情况:What is the best way to limit concurrency when using ES6's Promise.all()?

答案 18 :(得分:0)

在我的情况下,这是由于在使用 setInterval 调用的异步函数内创建它们时没有关闭与数据库的 Sequelize 连接。

答案 19 :(得分:-1)

EventEmitter.defaultMaxListeners = <MaxNumberOfClients> 添加到 node_modules\loopback-datasource-juggler\lib\datasource.js 已修复可能会出现问题:)

答案 20 :(得分:-4)

将它放在server.js的第一行(或包含主Node.js应用程序的任何内容):

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

并且错误消失了:)

相关问题