node.js服务内存与cpu分析

内存与cpu分析

V8的垃圾回收机制与内存限制

node.js的内存管理和JavaScript执行引擎V8息息相关。V8垃圾回收机制必须要控制内存的上限(一般64位系统1.4G、32位系统0.7G)。如果内存不限制,做垃圾回收引起JavaScript线程暂停时间过长,会使应用的响应能力直线下降。

查看v8内存信息

1
2
3
4
5
6
➜  ~ node
> process.memoryUsage();
{ rss: 21499904,
heapTotal: 7684096,
heapUsed: 4956992,
external: 16823 }

heapTotal和heapUsed是V8堆内存的使用情况。

查看系统总内存和闲置内存

1
2
3
4
5
6
➜  ~ node
> os.totalmem()
8589934592
> os.freemem()
281985024
>

可以看到,V8只能利用系统(如服务器)的一部分内存。

查看垃圾回收日志

内存利用可以分析垃圾回收日志
示例

1
2
3
4
// test.js
for (var i = 0; i < 1000000; i ++) {
var a = {};
}

执行命令

1
node -prof test.js

将生成的log文件重命名

1
mv isolate-0x103800000-v8.log v8.log

使用node-tick-processor npm包分析

1
node-tick-processor v8.log

得到的统计结果中垃圾回收信息如下:

1
2
3
[GC]:
ticks total nonlib name
3 5.1%

由于代码不断分配对象,垃圾回收所占时间为5.1%,显然是不合理的。

内存泄漏排查

我们通过工具来排查是否存在内存泄漏,这里以node-heapdump为例。
示例

1
2
3
4
5
6
7
8
9
10
11
12
13
var heapdump = require('heapdump');
var http = require('http');

var leakArray = [];
var leak = function () {
leakArray.push("leak" + Math.random());
};

http.createServer(function (req, res) {
leak();
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(1337);

通过向服务器进程发送SIGUSR2信号,可以不断抓拍堆内存快照。

1
kill -USR2 <pid>

快照会在目录下自动生成,在Chrome开发者工具-Memory-Profiles中导入快照,选择Comparison就可以对比前后几个快照的垃圾回收情况,从而定位内存泄漏问题。

1
2
3
4
concatenated
leak0.5140695987557682
leak0.48309920416566277
...

编码注意事项

慎用全局变量

它会常驻内存中,可以通过delete或赋值(推荐)释放它们。

1
2
3
4
global.foo = '1';
delete global.foo;
// 或
global.foo = undefined; // or null

减少闭包

中间函数作用域不会释放,会占用内存,直到不再有引用。

善用堆外内存

如Buffer对象,内存是C++管理的,不受V8堆内存的限制。

慎将内存当缓存

如果确实有必要,需要增加缓存限制策略。对于大量缓存,可以使用进程外的缓存,如结合Redis。

关注队列状态

如日志收集,若瞬间写入海量数据,会造成写入操作堆积,内存泄漏。可以采用堆积监控报警、异步调用超时机制等给消费速度设定下限值。

大内存应用

可以使用”流”来操作大文件。

0%