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】:我在使用 mocha 和 enzyme 测试 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.addListener
和 EventEmitter.on
进行原型设计。以上是关于NodeJS:如何调试“检测到 EventEmitter 内存泄漏。添加了 11 个侦听器”的主要内容,如果未能解决你的问题,请参考以下文章
如何将 NodeJS 调试器通过隧道传送到 Vagrant 框?
如何使用 PhpStorm / WebStorm 连接到远程 nodejs 调试会话?