V8的垃圾回收机制与内存限制
node.js的内存管理和JavaScript执行引擎V8息息相关。V8垃圾回收机制必须要控制内存的上限(一般64位系统1.4G、32位系统0.7G)。如果内存不限制,做垃圾回收引起JavaScript线程暂停时间过长,会使应用的响应能力直线下降。
查看v8内存信息
1 | ➜ ~ node |
heapTotal和heapUsed是V8堆内存的使用情况。
查看系统总内存和闲置内存
1 | ➜ ~ node |
可以看到,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
13var 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
4concatenated
leak0.5140695987557682
leak0.48309920416566277
...
编码注意事项
慎用全局变量
它会常驻内存中,可以通过delete或赋值(推荐)释放它们。1
2
3
4global.foo = '1';
delete global.foo;
// 或
global.foo = undefined; // or null
减少闭包
中间函数作用域不会释放,会占用内存,直到不再有引用。
善用堆外内存
如Buffer对象,内存是C++管理的,不受V8堆内存的限制。
慎将内存当缓存
如果确实有必要,需要增加缓存限制策略。对于大量缓存,可以使用进程外的缓存,如结合Redis。
关注队列状态
如日志收集,若瞬间写入海量数据,会造成写入操作堆积,内存泄漏。可以采用堆积监控报警、异步调用超时机制等给消费速度设定下限值。
大内存应用
可以使用”流”来操作大文件。