V8的垃圾回收机制与内存限制
在Node中,内存管理的好坏、垃圾回收状况是否优良都与JavaScript执行引擎V8息息相关。
V8的内存限制
Node在通过Javascript使用内存时只能使用部分内存(64位系统下约为1.4GB,32位系统下约为0.7GB)。
V8的对象分配
在V8中,所有的Javascript对象都是通过堆来进行分配的
- Node在启动时可以传递参数来调整内存限制的大小
1
2node --max-old-space-size=1700 test.js //单位为MB
node --max-new-space-size=1024 test.js // 单位为KB
V8的垃圾回收机制
- V8主要的垃圾回收算法
V8的垃圾回收策略主要基于分代式垃圾回收机制。
- 新生代中的对象主要通过Scaverge算法进行回收
- 老生代中的对象采用Mark-Sweep和Mark-Compact相结合方式进行垃圾回收
查看垃圾回收日志
- 查看垃圾回收的主要方式是在启动时添加
--trace_gc
参数。 - 在启动时使用
--prof
参数,可以得到V8执行时的性能分析数据。
高效使用内存
V8面前,开发者所要具备的责任是如何让垃圾回收机制更高效地工作。
作用域
在Javascript中能形成作用域的有
- 函数调用
- with
- 全局作用域
闭包
在正常Javascript执行中,无法立即回收的内存有闭包和全局变量引用。需要小心使用。
内存指标
查看内存使用情况
- 查看进程的内存占用
调用process.memoryUsage()
可以查看Node进程的内存占用情况,示例代码如下:
1 | $ node |
- rss: resident set size, 进程的常驻内存部分,进程的内存除了常驻内存部分还有swap部分、文件系统
- heapTotal: 堆中总共申请的内存量
- heapUsed: 堆中使用中的内存量
- 查看系统的内存占用
os模块中的totalmem()
和freemem()
,用于查看操作系统的内存使用情况。
- totalmem(): 返回系统的总内存
- freemem(): 返回系统的闲置内存
堆外内存
Node中的内存使用并非都是通过V8进行分配,那些不通过V8分配的内存称为堆外内存。
例如Buffer,并且堆外内存可以突破V8内存限制。
小结
Node的内存构成主要由通过V8进行分配的部分和Node自行分配的部分。受V8的垃圾回收限制的主要是V8的堆内存。
内存泄漏
内存泄漏的情况不尽相同,但其实质只有一个:
应当回收的对象出现意外而没有被回收,变成了常驻在老生代中的对象。
通常,造成内存泄漏的原因如下:
- 缓存
- 队列消费不及时
- 作用域未释放
慎将内存当做缓存
Node中,任何试图拿内存当缓存当行为都应当被限制。拿内存当缓存可以理解为在使用全局变量当缓存。
缓存当解决方案
目前较好当解决方案是采用进程外的缓存,进程自身不存储状态。
- 缓存转移到外部,减少常驻内存的对象的数量,让垃圾回收更高效
- 进程之间可以共享缓存
- redis、memcached
关注队列状态
队列同样也会在不经意间产生内存泄漏。
表层解决方案是换用消费速度更高的技术;
深度的解决方案是监控队列的长度;
内存泄漏排查
常用工具:node-heapdump、node-memwatch。
他俩各有所长,可以结合起来使用。
大内存应用
Node提供stream模块用于处理大文件。不受V8堆内存的影响,提供管道方法pipe()
。