手机版

Node.js内存泄漏分析

时间:2021-09-09 来源:互联网 编辑:宝哥软件园 浏览:

这篇文章是《节点》的第一篇。Mozilla的Identity团队带来的JS假日季系列,上个月发布了Persona的第一个测试版。在开发Persona时,我们构建了一系列工具,包括调试、本地化、依赖管理等等。在这一系列文章中,我们将与社区分享我们的经验和这些工具,这对任何想要用node.js构建高可用性服务的人来说都非常有用。我们希望您会喜欢这些文章,并期待看到您的想法和贡献。

我们将从一篇关于Node.js的实质性问题:内存泄漏的主题文章开始。我们将介绍Node-memwatch,这是一个帮助查找和隔离Node中内存泄漏的函数库。

何必自寻烦恼呢?

关于跟踪内存泄漏,最常见的问题是,“为什么要麻烦自己?”。难道没有更迫切的问题需要先解决吗?为什么不选择不时重启服务,或者为其分配更多的RAM?为了回答这些问题,我们提出以下三点建议:

1.也许你不关心不断增加的内存使用,但是V8关心(V8是节点运行时的引擎)。随着内存泄漏的增加,V8对垃圾收集器的攻击性越来越强,这会让你的应用变慢。因此,在节点上,内存泄漏会损害程序性能。

2.内存泄漏可能会触发其他类型的故障。内存泄漏的代码可能会持续引用有限的资源。您可能用完了文件描述符;您也可能突然无法建立新的数据库连接。这种问题可能会在应用程序内存耗尽之前很久就暴露出来,但它仍然会给你带来麻烦。

3.最后,您的应用程序迟早会崩溃,当您的应用程序流行时,这种情况肯定会发生。大家都会在黑客新闻上嘲笑你,讽刺你,让你苦不堪言。

破千里堤的蚁巢在哪里?

构建复杂的应用程序时,许多地方可能会发生内存泄漏。闭包可能是最广为人知和臭名昭著的。因为闭包保留了对其范围内事物的引用,这是内存泄漏的常见来源。

只有当有人在寻找时,才会发现密封泄漏。然而,在Node的异步世界中,我们随时随地通过回调函数不断生成闭包。如果这些回调函数在创建后没有立即使用,分配的内存将继续增长,似乎没有内存泄漏问题的代码也会泄漏。而这种问题更难发现。

由于上游代码问题,您的应用程序也可能会泄漏内存。也许你可以找到内存泄漏的代码,但你可能只会盯着你完美的代码,想知道它是如何泄漏的!

正是这些难以定位的内存泄漏让我们想要一个像node-memwatch这样的工具。传说几个月前,我们的劳埃德希莱尔把自己锁在一个小房间里两天,试图追查一个在压力测试下变得非常明显的内存泄漏问题。顺便说一下,请期待劳埃德即将发表的关于负载测试的文章

经过两天的努力,他终于在Node内核中找到了罪魁祸首:http中的事件监听器。ClientRequest未发布。(最终修复此问题的补丁只有两个但至关重要的字母。).正是这段痛苦的经历促使劳埃德编写了一个工具,可以帮助查找内存泄漏。

内存泄漏定位工具

Node.js应用程序中有许多易于使用且不断增强的工具来定位内存泄漏。以下是其中的一些:

Jimmercer的node-mtrace,它使用GCC的mtrace工具来分析堆的使用情况。戴夫帕切科的节点堆转储获取了V8堆的快照,并将所有内容序列化到一个巨大的JSON文件中。它还包含一些用于分析和研究快照结果的JavaScript工具。Danny Coates的v8-profiler和节点-inspector提供了绑定在node中的v8分析器和基于WebKit Web Inspector的调试界面。菲利克斯格纳斯的无限守护者图形分支。Felix Geisendorfer的Node内存泄漏教程是一个使用v8-profiler和node-debugger的简短而酷的教程。同时也是调试Node.js内存泄漏最先进的技术指南。Joyent的SmartOS平台提供了大量调试Node.js内存泄漏的工具。我们都喜欢这些工具,但它们都不适合我们的场景。Web Inspector非常适合开发应用程序,但很难在热部署中使用,尤其是当涉及多个服务器和子流程时。同样,在长期高负载运行中,也很难再现内存泄漏。像dtrace和libumem这样的工具令人印象深刻,但并不是所有的操作系统都能使用它们。

输入节点-memwatch

我们需要一个跨平台调试库。当我们的程序可能有内存泄漏时,它不需要设备告诉我们,它会帮助我们找到哪里有泄漏。因此,我们实现了node-memwatch。

它为我们提供了三样东西:

“泄漏”事件发送器

memwatch.on('leak ',function(info) { //查看info,了解可能泄漏的内容});“状态事件发射器”

var memwatch=require(' memwatch ');memwatch.on('stats ',function(stats) { //对gc后内存使用情况stats }做点什么);堆内存区域的分类

var hd=新memwatch。HeapDiff();//您的代码在这里.var diff=HD . end();还有一个函数在测试中很有用,可以触发垃圾收集器。好了,四点了。

var stats=memwatch . GC();Memwatch.on ('stats ',):垃圾收集后堆统计信息

Node-memwatch可以在分配任何JS对象之前,在完成垃圾收集和内存压缩之后发送一个内存使用示例。(它使用V8的gc后挂钩v 8:3360 addgcepiloguecallback在每次垃圾收集触发时收集堆使用信息。)

统计数据包括:

usage _ trend current _ base estimated _ base num _ full _ GC num _ Inc _ GC(增加的垃圾收集次数)heap_compactions min(最小值)max(最大值)这是一个显示内存泄漏的应用程序。下图跟踪了一段时间内的内存使用情况。疯狂的绿色线条显示了process.memoryUsage()报告的内容。红线显示node_memwatch报告的current_base。左下角的框显示了附加信息。

2015623152204606.png  (572441)

注意非常高的Incr GCs。这意味着V8正在拼命清理内存。

Memwatch.on('泄漏',):堆分配趋势

我们定义了一个简单的检测算法来提醒您应用程序中可能存在内存泄漏。也就是说,如果连续五次垃圾收集后内存被连续分配但没有释放,node-memwatch将发出一个泄漏事件。事件的具体信息格式清晰可读,就像这样:

{ start: Fri,2012年6月29日14:12:13 GMT,end: Fri,2012年6月29日14:33 GMT,growth: 67984,Reason : '堆增长超过5个故意GCS(20s)-11.67 MB/HR ' } memwatch . heapdiff():查找泄漏罪魁祸首

最后,node-memwatch可以将堆上对象的名称与分配数量的快照进行比较,比较前后的差异有助于找出导致内存泄漏的罪魁祸首。

var hd=新memwatch。HeapDiff();//您的代码在这里.var diff=HD . end();这种比较产生了如下结果:

{ '在' :之前{ 'nodes': 11625,' size_bytes': 1869904,' size': '1.78 mb' },'在' :之后{ 'nodes': 21435,' size_bytes': 2119136,' size': '2.02 mb' },' change ' : { ' Size' :'-2.07 KB ',' : 3 ',-': 62}]}} Heapdiff方法会在采样数据之前做一个完整的垃圾收集,这样数据就不会充满无用的信息。 memwatch的事件处理忽略了HeapDiff触发的垃圾收集事件,所以可以在stats事件的监控回调函数中安全地调用HeapDiff方法。

版权声明:Node.js内存泄漏分析是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。