在Node.js的内存泄漏分析
本文是系列文章的一个js假期由Mozilla的身份队带来了第一个问题。团队角色的第一个测试版发布的最后一个月。发展中的角色,我们建立了一系列的工具,从调试、定位,要依赖管理,等等。在这一系列文章中,我们将与社区分享我们的经验和工具,这对于那些想与node.js.we希望你会喜欢这些文章,期待看到你的想法和贡献,建立一个高可用性服务是非常有用的。我们先对Node.js的实质性问题的一个主题:内存泄漏。我们将介绍节点MEMWATCH -一个函数库,有助于发现并隔离在节点的内存泄漏的问题。
何必呢
在轨道内存泄漏最常被问到的问题是,为什么要麻烦难道没有第一个迫切需要解决的问题吗为什么不经常重新启动服务,或者为它分配更多的RAM呢为了回答这些问题,我们提出以下三点建议:
1。也许您不关心内存占用的增加,但V8关心(V8是节点运行的引擎)。随着内存泄漏的增加,V8对垃圾收集器越来越有攻击性,这会减慢应用程序的速度。因此,在节点上,内存泄漏会损害程序性能。
2。内存泄漏可能触发其他类型的故障。内存泄露的代码可能会继续引用有限的资源。您可能用完了文件描述符,也可能突然无法建立一个新的数据库连接。这种问题可能会在您的应用程序内存耗尽之前很早就暴露出来,但它仍然会给您带来麻烦。
三.最后,应用程序会崩溃是迟早的事,当然也会发生在你的应用程序是受欢迎的。所有的人都会嘲笑你的黑客新闻,讽刺你,你将是悲惨的。
打破在千里之堤巢吗
在构建复杂的应用程序时,内存泄漏在许多地方都可能发生,闭包可能是最广为人知和最臭名昭著的,因为闭包保持在其范围内的引用,这是通常内存泄漏的来源。
闭包是经常发现只有当有人找他们。但在节点异步的世界,我们生成时间都关闭和随时随地通过回调函数。如果这些回调函数不使用创建后立即被分配的内存将继续增长,而这些代码,似乎没有内存泄漏问题也会泄漏。这个问题是比较难找到。
由于上游代码的问题,您的应用程序也可能导致内存泄漏。也许您可以找到内存泄漏代码,但您可能只盯着您的代码,而最终的困惑是如何泄漏的!
正是这些努力-查找内存泄漏,使我们想node-memwatch.legend工具已经在几个月前,我们的Lloyd Hilaiel把自己锁在一个小房间里两天,试图跟踪内存泄漏非常明显的压力测试。(顺便说一句,请期待劳埃德的到来负载测试文章)
经过两天的努力,他终于发现在节点核心的罪魁祸首:在http.clientrequest事件监听器没有公布。(只有两个补丁,但关键字母,最终解决问题)。正是这种痛苦的经验,促使劳埃德写的一个工具,可以帮助找到内存泄漏。
内存泄漏定位工具
现在有许多有用的和不断增强的工具来定位Node.js应用程序的内存泄漏。以下是其中的一些:
Jimb Esser的结mTrace,使用GCC mtrace工具分析堆的使用。
Dave Pacheco的节点堆转储捕获了V8堆的快照,并将所有内容序列化为一个巨大的JSON文件,它还包含了一些分析快照结果的Javascript工具。
V8的轮廓和Danny Coates节点督察提供V8分析仪结合节点和调试基于WebKit Web Inspector界面。
Felix Gnass不禁用人图的分支。
节点的内存泄漏(节点的内存泄漏教程)Felix Geisendorfer是一个短的和酷的教程和node-debugger.at V8探查的同时,它也是最先进的js调试内存泄露的技术指南。
Joyent的SmartOS的平台,这对于调试js内存泄漏提供了大量的工具。
上面的工具,我们都很喜欢,但没有一个场景。Web Inspector是非常适合应用在发展,但很难用热部署方案,尤其是当多个服务器和子过程。同样,内存泄漏长期高负荷运行也难以再现过程中发生的。工具如DTrace和libumem令人印象深刻,但并不是所有的操作系统都可以使用。
enternode MEMWATCH
我们需要一个跨平台的调试库。当我们的程序可能有内存泄漏,不需要设备来告诉我们,它将帮助我们找到哪里有泄漏,所以我们实现了节点MEMWATCH。
它为我们提供了三件事:
泄漏事件发射器
Memwatch.on('leak功能(信息){
查看信息,找出可能泄漏的信息。
});
状态事件发射器
VaR MEMWATCH =需要('memwatch);
Memwatch.on('stats功能(统计){
使用GC后内存使用情况做一些事情
});
堆内存区域分类
VAR高清=新memwatch.heapdiff();
这里的代码…
VaR差异= hd.end();
还有一个有用的测试,可以触发垃圾收集器的功能。好的,四。
var属性= memwatch.gc();
Memwatch.on('stats ',…):后GC堆统计
可以在任何节点MEMWATCH JS对象分配问题的一个完整的垃圾收集和存储压缩内存使用的样品。(它使用V8后GC钩,V8::addgcepiloguecallback,收集堆使用的信息时,触发垃圾回收一次)
统计数据包括:
Usage_trend (use trend)
current_base(目前的基数)
estimated_base(预计基地)
num_full_gc(垃圾收集次数完成数)
num_inc_gc(垃圾收集次数增加)
heap_compactions(内存压缩数)
最小(最小)
马克斯(最大值)
这是什么样的应用程序有内存泄漏的数据看起来像一个例子。下面的图表跟踪内存的使用时间。疯狂的绿色线显示process.memoryusage(报告),红线显示的node_memwatch报告current_base。在左下框中显示额外信息。
注意提高GCS评分很高。这意味着V8正在努力清理内存。
Memwatch.on('leak ',…):堆分布趋势
我们已经定义了一个简单的检测算法来提醒你的应用可能存在内存泄漏,如果记忆仍不断分配和连续五个GC后不释放,结MEMWATCH将发送一个泄漏事件。该事件的具体信息,格式清晰易读,像这样:
{开始:星期五,2012年6月29日14:12:13 GMT,
结束:星期五,2012年6月29日14:12:33 GMT,
增长:67984,
原因:在5个连续的GCS 'heap生长(20S)- 11.67 MB /人力资源}
Memwatch.HeapDiff():查找元凶的泄漏
最后,节点MEMWATCH可以比较的名称、对象快照数堆上。它们之间的区别可以帮助识别导致内存泄漏的罪魁祸首。
VAR高清=新memwatch.heapdiff();
这里的代码…
VaR差异= hd.end();
对比的内容是这样的。
{
之前:{
节点:11625,
size_bytes :1869904,
大小:1.78 MB
},
后:{
节点:21435,
size_bytes :2119136,
大小:2.02 MB
},
更改:{
size_bytes :249232,
大小:243.39 KB
freed_nodes :197,
allocated_nodes :10007,
详细信息:{
{
:数组
size_bytes :66688,
大小:65.13 KB
+:4,
78:
},
{
什么:代码
size_bytes :55296,
大小:- 54 KB
+:1,
57:
},
{
什么leakingclass
size_bytes :239952,
大小:234.33 KB
+:9998,
0:
},
{
什么:字符串
size_bytes :2120,
大小:-2.07 KB
+:3,
62:
}
}
}
}
heapdiff方法之前的数据采样执行完整的垃圾收集是这样执行,得到的数据将不被充满了太多的无用信息。MEMWATCH的事件处理忽略了垃圾收集事件所引发的HeapDiff,所以你可以放心的在统计事件的侦听回调函数调用heapdiff方法。