NodeJS:如何调试“检测到 EventEmitter 内存泄漏。添加了 11 个侦听器”

Posted

技术标签:

【中文标题】NodeJS:如何调试“检测到 EventEmitter 内存泄漏。添加了 11 个侦听器”【英文标题】:NodeJS : How to debug "EventEmitter memory leak detected. 11 listeners added" 【发布时间】:2013-03-12 23:54:56 【问题描述】:

如何调试抛出此错误的应用程序:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at Socket.EventEmitter.addListener (events.js:160:15)
    at Socket.Readable.on (_stream_readable.js:653:33)
    at Socket.EventEmitter.once (events.js:179:8)
    at TCP.onread (net.js:527:26)

我找不到假设的泄漏对象,用于将侦听器限制增加.setMaxListeners(0);

解决方案(来自 fardjad 和 jan salawa)

通过 jan salawa 的搜索,我找到了一个工作库 (longjohn),用于增加堆栈跟踪的详细程度。根据 fardjad 的回复,我发现我们必须对 EventEmitter.addListener AND EventEmitter.on 进行原型设计。

有了这个解决方案,我可以得到这个新的踪迹:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at EventEmitter.addListener.EventEmitter.on (xxx/main.js:44:15)
    at Readable.on (_stream_readable.js:653:33)
    at ServerResponse.assignSocket (http.js:1072:10)
    at parser.onIncoming (http.js:1979:11)
    at parserOnHeadersComplete (http.js:119:23)
    at socket.ondata (http.js:1912:22)
    at TCP.onread (net.js:510:27)

【问题讨论】:

查看有关 agent.maxSockets weblog.bocoup.com/node-stress-test-analysis 的信息。也许这是一个原因。 试过没有成功。我将require("http").globalAgent.maxSockets = Infinity; 放在我的main.js 中,但没有任何改变... :( 我还尝试了ulimit -n 999999 命令来限制打开文件,-–nouse-idle-notification 用于暂停实时垃圾收集器... 当我按下 control + C 其他处理开始时,这完全挂起我的节点 js 服务器,就像在本地运行时出现的异常一样。我试过设置 maxListeners 没用,这是说我有太多人连接到同一个听众还是说我有太多事件 【参考方案1】:

如果您注册同一对象的特定事件超过 11 次,则会引发此警告。

检查您是否在经常调用的函数中对特定事件进行“on”调用,这会导致多次注册事件。

This 链接帮助我理解了这一点。

【讨论】:

应该是“10次以上”。 谢谢,这有帮助!我有一个带有 play() 和 stop() 方法的应用程序。我有一个 EventEmitter 在调用 stop() 时发送一个“停止信号”,这会杀死一个进程(视频播放器)。问题是如果我调用 play() 11 次:该进程似乎将所有播放器进程注册为仍然打开(即使它们没有),所以我需要在启动新进程之前杀死任何进程。【参考方案2】:

我在运行单元测试时看到了这一点。我的单元测试反复调用正在调用的代码:

process.on("uncaughtException", () =>  ... );

我不得不使用依赖注入来注入一个伪造的进程对象,从而解决了这个问题。

main.js:

export function main(myProcess) 
    myProcess.on("uncaughtException", () =>  ... );


if (require.main === module)  // to prevent this from executing when running unit tests
    main(process);

我的单元测试会做:

const fakeProcess = jasmine.createSpy("process", ["on"]);
main(fakeProcess);

【讨论】:

【参考方案3】:

原来这是nodejs核心的一个bug,我们在这里讨论这个问题:https://github.com/joyent/node/issues/5108

错误的 http 服务器的解决方案,即抛出 EventEmitter memory leak detected 并填满可用内存/可用 CPU 时间:

恢复到旧版本v0.8.23。 (您可以从这里下载并安装/编译它:http://blog.nodejs.org/2013/04/08/node-v0-8-23-legacy/)

2018 年更新:我看到了一些关于这个主题的反馈,而这个问题似乎已经消失了多年。请注意,此响应仅适用于使用 nodejs 构建泄漏的 http 服务器。如果您在其他情况下,请查看此线程上的其他回复并且不要降级您的版本(如此回复所建议的那样),您将浪费您的时间。

【讨论】:

一旦他们确定解决了这个问题,我会尽量让每个人都发布在更新的 node 工作版本上。我仍然遇到问题。 为什么在v0.10.24还有bug的时候就关闭了!? @schlingel 仍在 v0.10.26 中 我在 v0.10.32 中看到了这个 也出现在 v0.10.33 中【参考方案4】:

从节点 6 开始,您应该使用 node --trace-warnings: https://nodejs.org/api/cli.html#cli_trace_warnings

【讨论】:

或者,还有github.com/niftylettuce/max-listeners-exceeded-warning【参考方案5】:

我在使用 mochaenzyme 测试 React 组件时遇到了同样的问题。

在完成测试后,我能够通过显式卸载组件来解决我的问题。

问题是我在测试中多次安装组件,然后添加了更多的侦听器,直到侦听器的数量达到 11,我收到了警告。

我通过添加 rendered.unmount() 行更改了我的测试代码。这解决了我的问题。

describe('<CircleArc />', () => 

    it('renders', function () 
        const rendered = mount(<CircleArc />);
        assert.ok(rendered.find('path'));
        rendered.unmount();
    );

【讨论】:

【参考方案6】:

这正是发生在我身上的事。对我来说,我不小心将一个事件监听器嵌套在另一个事件监听器中。

查看您的代码并确保在另一个事件侦听器块中没有事件侦听器块(例如,除非您是故意这样做的):

socket.on('data', function(data) 
//code goes here

socket.on('close' , function() 
//code goes here
     );

   );

在上面的错误示例中,socket.on ('close') 监听器应该在 socket.on('data') 块之外。

就我而言,当我收到 5 个数据流时,socket.on('close') 侦听器正在等待关闭事件发生。当我关闭一次时,将执行另一个第四次关闭事件。这显然不是我想要的。这是由于 Node.js 的非阻塞性质。由于回调函数,它会“记住”事件。

【讨论】:

这也是我错误的根源!我有somereaderstream.on("close", function() fstream.Reader( 'path': basepath, 'type': 'Directory' ).on("end", function() ..... ); ); Thanks for pointing out!【参考方案7】:

对我来说,您的事件循环似乎被阻止了。如果您在 node.js 事件循环中执行 cpu 密集型任务,则可能会发生这种情况。您可以使用child process 来执行密集型任务。

您可以使用 following methods 检查阻止 node.js 的内容:

    Measure computation time 每次通话。如果时间很长,请记录下来,以便您知道应用行为不端。 设置日志计划,以便您知道什么时候出现阻塞循环
    function timeTick() 
        var startTime = (new Date().getTime());
        function onTick() 
            var interval = (new Date().getTime()) - startTime;
            if(interval > 5)
                console.log('timeTick(): WARNING: interval = ' + interval);
        
       process.nextTick(onTick);
    
    setInterval(timeTick, 1000);
    使用profile. 使用this 进行日志记录和分析。 Nodejitsu 中使用的库。

【讨论】:

是的,这个问题来自于在密集任务时添加了太多侦听器的对象。但我必须找到女巫对象并增加听众限制!问题是如何找到问题出在哪里? 我不确定您是否正在执行 CPU 密集型任务。我已经编辑了我的帖子。 当密集型任务(处理许多连接)时,CPU 上升不超过一个核心的 20%。但是错误发生后,程序开始使用 100% CPU 和不断增长的内存。 好的,你能展示一下你是如何在服务器端处理这些连接的吗? 我不知道仅导出连接处理是否会对您有所帮助。我认为问题也可能来自连接依赖...【参考方案8】:

我尝试制作 EventEmitter 的原型,以便将日志消息添加到 addListener 但我无法让它工作

要钩住addListener,你可以这样做:

// on the first line of your main script
var events = require("events"),
    EventEmitter = events.EventEmitter;

var originalAddListener = EventEmitter.prototype.addListener;
EventEmitter.prototype.addListener = function (type, listener) 
    if (this.listenerCount(this, type) >= 10) 
        // TODO: PLACE YOUR CODE FOR DEBUGGING HERE
    
    originalAddListener.apply(this, arguments);

【讨论】:

它不起作用。我的一些应用程序侦听器调用了该钩子,但不是所有侦听器。不幸的是,addListener 女巫触发错误并没有执行钩子(我试图记录所有侦听器删除条件)。这个钩子是否附加在模块 events.EventEmitter.addListener 上? (也许问题来自我正在使用的“坏”模块?) 这个问题可能是模块坏了,但是node.js缓存了模块。多次尝试require 一个模块(通常)加载该模块一次。所以如果你在你的主脚本上安装钩子,其他模块应该使用钩子addListener 你是对的,我试图将你的代码放在一些模块中,结果是一样的:我在我的应用程序开始时在不同的对象上获得了 20/30 个侦听器,当错误出现时什么也没有:(。 为了真正发挥作用,我们必须同时对 EventEmitter.addListenerEventEmitter.on 进行原型设计。

以上是关于NodeJS:如何调试“检测到 EventEmitter 内存泄漏。添加了 11 个侦听器”的主要内容,如果未能解决你的问题,请参考以下文章

如何设置nodejs调试模式不听127.0.0.1

如何将 NodeJS 调试器通过隧道传送到 Vagrant 框?

如何使用 PhpStorm / WebStorm 连接到远程 nodejs 调试会话?

NodeJS:如何调试“检测到 EventEmitter 内存泄漏。添加了 11 个侦听器”

如何调试nodejs的二进制模块?

如何使用 node-inspector 调试 nodejs 后端?