JavaScript与Java一样,采用垃圾回收机制进行自动内存管理。
Node基于V8,因此受到V8的一些限制:只能使用部分内存(64位系统下约1.4GB,32位系统下约为0.7GB)。在单个Node进程的情况下,计算机的内存资源无法得到充足的使用。
在V8中,所有JavaScript对象都是通过堆来进行分配的。查看内存信息:
$ node
> process.memoryUsage();
{ rss: 26107904,
heapTotal: 6733824,
heapUsed: 4678648,
external: 9288 }
其中:heapTotal是申请到的堆内存,heapUsed是当前堆内存使用量。
堆分配过程: 声明变量并赋值 --> 所使用对象的内存分配在堆中 --> 如果申请到的堆空闲内存不够则继续申请,直到堆的大小超过V8的限制为止
要调整堆内存限制的大小,在Node启动时传入参数--max-old-space-size或者--max-new-space-size:
node --max-old-space-size=1700 test.js //单位为MB
// or
node --max-new-space-size=1024 test.js //单位为KB
按对象的存活时间将内存分为新生代(new space)和老生代(old space):
垃圾回收算法需要应用逻辑暂停,待回收后再恢复执行,即“全停顿”(stop-the-world)。为了降低全堆垃圾回收带来的停顿时间,V8在标记阶段采用增量标记(incremental marking)将垃圾回收拆分成许多小“步进”,每完成一次“步进”后执行应用逻辑一小会儿,再执行“步进”,依次交替重复直到标记阶段完成。
还引入了延迟清理(lazy sweeping)与增量式整理(incremental compaction)等方式提高性能。
可以总结出:
V8堆 = 新生代(new space) + 老生代(old space)
新生代(new space) = semi space(From) + semi space(To)
--- | new space | old space | V8堆大小 |
---|---|---|---|
--- | semi space * 2 | --- | --- |
64位系统 | 32MB * 2 | 1400MB | 1464MB |
32位系统 | 16MB * 2 | 700MB | 732MB |
在启动时添加--trace_gc参数,可以得到垃圾回收执行情况:
node --trace_gc test.js > gc.log
在启动时添加--prof参数,可以得到V8执行时的性能分析数据:
node --prof test.js
执行命令之后,会在该目录下产生一个 *-v8.log 的日志文件,我们可以安装一个日志分析工具 tick:
$ npm install tick -g
$ node-tick-processor *-v8.log
[Top down (heavy) profile]:
Note: callees occupying less than 0.1% are not shown.
inclusive self name
ticks total ticks total
426 36.7% 0 0.0% Function: ~<anonymous> node.js:27:10
426 36.7% 0 0.0% LazyCompile: ~startup node.js:30:19
410 35.3% 0 0.0% LazyCompile: ~Module.runMain module.js:499:26
409 35.2% 0 0.0% LazyCompile: Module._load module.js:273:24
407 35.1% 0 0.0% LazyCompile: ~Module.load module.js:345:33
406 35.0% 0 0.0% LazyCompile: ~Module._extensions..js module.js:476:37
405 34.9% 0 0.0% LazyCompile: ~Module._compile module.js:378:37
也可以使用 headdump 之类的工具将日志导出,然后放到 Chrome 的 Profile 中去分析。
在正常的JavaScript执行中,无法立即回收的内存有闭包和全局变量引用这两种情况。此类变量会导致老生代中的对象增多,由于V8的内存限制,因此要注意此类变量是否无限制的增加。
Node的内存由V8堆内存和Node分配的堆外内存构成。V8的垃圾回收限制的主要是的V8的堆内存。Node自行分配的堆外内存不受此限制。
通常,造成内存泄漏的原因有:
使用工具,例如:node-heapdump,node-memwatch等工具来分析堆内存,来排查内存泄漏的原因。
如果不可避免要操作大文件,请使用Node提供的stream,buffer等模块来处理。
使用时仍然要注意物理内存的限制。