Node.js排除内存泄漏演示

3,068 阅读2分钟

用于定位node内存泄漏的工具常用的有:

在演示DEMO之前可以了解点基本前置知识

关于Node的堆内存与堆外内存小记。

  1. 受到V8 GC的主要是堆内存。
  2. Node中的内存并非都是通过V8来进行分配的,可通过堆中的内存用量(heapUsed)总是小于进程常驻内存(rss)看出。 对于那些不是通过V8来分配内存的我们称为堆外内存,比如Buffer,Buffer是基于C++的不是V8(从下面的demo中rss与external的值可以看出)。 external代表 V8 管理的,绑定到 Javascript 的 C++ 对象的内存使用情况。
  3. 因此表明堆外内存是可以突破内存限制问题。

使用node-heapdump排除内存泄漏的DEMO

这里主要演示node-heapdump, node-memwatch很久没有维护了。

环境安装

  • node.js
  • node-gyp
  • mac下需要安装 xcode-select --install
  • mac下安装xcode后 sudo xcode-select --switch /Library/Developer/CommandLineTools/
  • node-heapdump

代码


const heapdump = require('heapdump')
const http = require('http')

const leakArray = []
const leak = function() {
	leakArray.push(`leak: ${Math.random()}`)
}

http.createServer(function(req, res) {
	// 每次访问node服务。都会往leakArray增加数据,并且不会回收。
	leak()
	res.writeHead(200, {'Content-Type': 'text/plain'})
	res.end('hello node')
}).listen(9999)

console.log('server is running at: http:127.0.0.1:9999/')

然后我们通过在终通过curl不断访问服务器curl http://127.0.0.1:9999模拟用户访问,这个时候leakArray数组不断增大,并且不会被回收。

在UNIX平台上,你可以向服务器进程通发送SIGUSR2信号强制快照。


$ kill -USR2 <pid>

mac下查看pid

$ lsof -i tcp:9999

这个时候会在你的文件夹生成一个快照,文件名格式默认为:heapdump-..heapsnapshot。

由于Node是依赖V8引擎执行JavaScript的,Chrome浏览器也是,所以我们可以借助Chrome开发者工具中的Memory模块来分析这些dump文件。首先打开Chrome的开发者工具,切换到Memory,并依次加载dump文件。

关于Summary这个选项,从图中可以看出它有Constructor、Distance、Shallow Size、Retained Size共4项。Constructor这列是用类名对变量进行分组;Distance表示和根对象的距离,越小表示和根对象越近;Shallow Size表示变量自身的大小,不包含它引用的变量的大小;Retained Size不仅包含自身的大小,还包含了引用的变量的大小。

从图中可以看出,(string)那一行的Shallow Size和Retained Size都占据了100%的内存。

我们直接查看distance(20)最远的那一行。

然后我们详情看下leak()。

当你再详细查看leakArray你就发现这里存储了大量的leak字符串,它们一直未得到回收。

参考资料

  • 深入浅出node.js

PS: 感兴趣的可以关注下公众号。