利用 devtool工具定位Node内存泄漏

devtool 来自Chrome的谷歌开发者工具,基于 Electron 将 Node.js 和 谷歌开发者工具的功能融合在了一起,是一种Node.js调试工具。早期检测Node代码,主要是用在代码中使用heapdump生成内存快照,然后再打开Chrome浏览器的开发者工具导入生成好的内存快照文件。devtool的环境只不过是把Node环境改到浏览器环境中执行代码。

全局安装devtool

cnpm install devtool -g

定义内存泄漏实战

这里我们使用典型的沃尔玛内存泄漏代码,进行检测.
新建index.js文件,在index.js文件中输入如下代码:

var theThing = null
var replaceThing = function () {
var originalThing = theThing
var unused = function () {
if (originalThing) console.log("hi")
}
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage)
}
};
};
setInterval(replaceThing, 20)

上述代码应用了闭包,代码中unused的函数中持有了originalThing 的引用, 使得每次旧的对象不会释放从而导致内存泄漏。
OK,既然已经知道这个代码会有内存泄漏的问题,那么如何通过devtool工具来查找这个错误。
执行如下代码:

devtool index.js

出现如下devtool界面,并切换到顶部标签Profiles,并要快速生成三次快照。第一次快照是为了获取正常情况下的堆栈信息,而在问题出现后,堆栈信息一定会发生变化,有了第一次的信息,我们才好进行后面的比对,过滤一些无用的信息。而后两次的快照,用来比对某一对象的堆栈变化,来确定是否是有问题的对象。
这里说下为什么要快速生成,因为用devtool打开该index.js,不久之后就会出现内存爆满的错误,如下图。那时候再要生成快照,就已经没有办法,只能重新打开。

给出的内存快照中,有四种视图,我主要切换到Comparison视图,可以方便地定位问题。查看当前视图下的New列表和Delta列表,可以看到三次快照中每次都会New出很多数据。既然我们知道上诉代码是闭包中引用的对象没有释放的原因,我们直接打开(closure),可以看到每次快照中会多出很多someMethod()数据。

重新执行上面命令,重新打开devtool,切换顶部标签Timeline,
查看运行时的 memory timeline,如果图像呈现阶梯式增长,一般就是存在内存泄漏问题了.

这里给出测试代码的修复版本,试着再次生成三次快照和memory timeline,查看正常代码和非正常代码的区别

var theThing = null
var replaceThing = function () {
var originalThing = theThing
var unused = function () {
if (originalThing) console.log("hi")
}
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage)
}
};
};
setInterval(replaceThing, 20)