看完本文你将学到什么
- v8 堆内存的查看、限制、设置
- v8 堆内存在不同 Node 版本的表现
- v8 堆内存参数说明
关于 v8
关于 V8 在浏览器和 Node 中扮演的角色
v8 的堆内存限制
Node 程序中 javascript 的使用内存是有限制的,注意这个内存是指堆内存,在v8中,所有的 js 对象都存在堆中。在实际应用中不小心触碰到这个边界,进程就会退出。64位系统下为1.4GB,32位系统下为0.7GB。
Node 是基于 v8 引擎构建,所以 v8 会通过自己的方式来进行内存的分配和管理。
那么问题来了:
- 为什么要对 v8 堆内存进行限制?
- 为什么内存的大小控制在 1.4GB or 0.7GB ?
v8 的对象分配
内存查询
Node 提供了 process.memoryUsage() 方法,返回描述 Node.js 进程的内存使用情况(以字节 Bytes 为单位)的对象。
执行以下代码可以查看内存的使用情况:
$ node
> process.memoryUsage()
{
rss: 29196288,
heapTotal: 5685248,
heapUsed: 4549120,
external: 926086,
arrayBuffers: 10077
}
复制代码
解释一下这些字段:
rss: 常驻集大小, 是为进程分配的物理内存(总分配内存的子集)的大小,包括所有的 C++ 和 JavaScript 对象与代码。
heapTotal: v8 已申请的堆内存大小。
heapUsed: 当前堆内存已使用的大小。
external: 代表 V8 管理的绑定到 Javascript 对象的 C++ 对象的内存使用情况。
arrayBuffers: 代表分配给 ArrayBuffer 和 SharedArrayBuffer 的内存,包括所有的 Node.js Buffer。
在实际业务中,我们声明的每一个变量对象都被分配在堆中,如果已申请的堆中空闲内存不够分配新的对象,将继续申请堆内存,直到超出 v8 的限制。
那为什么 v8 要限制堆的大小呢? ?️
原因是 v8 的垃圾回收机制的限制。在 Node 中,垃圾回收是 v8 自动执行的,而 Node 又是单线程,所以在回收的过程中 js 线程会阻塞。以1.5G的垃圾回收堆内存为例,v8做一次小的垃圾回收需要 50ms 以上。一次非增量式的垃圾回收甚至需要一秒以上,这个时间损耗会严重影响程序的性能和响应能力。因此,出于性能考虑,v8 的堆内存限制和其大小的默认临界值也就可以理解了。
内存上限默认值
在《深入浅出Nodejs》中说明,64位系统约为1.4GB,32位系统约为0.7GB。
我根据本地几个 node 版本亲自测试了一下:
创建 memorySize.js:
const v8 = require('v8')
console.log('HeapStatistics:',v8.getHeapStatistics()) // 查询堆内存上限设置
复制代码
在 node 10.12.0 版本中,打印如下:
HeapStatistics: { total_heap_size: 6537216,
total_heap_size_executable: 1048576,
total_physical_size: 5230576,
total_available_size: 1520889432,
used_heap_size: 3865464,
heap_size_limit: 1526909922, // 1.42G
malloced_memory: 8192,
peak_malloced_memory: 416320,
does_zap_garbage: 0 }
复制代码
在 node 12.14.1 版本中,打印如下:
HeapStatistics: {
total_heap_size: 4472832,
total_heap_size_executable: 524288,
total_physical_size: 3317184,
total_available_size: 2194939360,
used_heap_size: 2277872,
heap_size_limit: 2197815296, // 2.04G
malloced_memory: 8192,
peak_malloced_memory: 127176,
does_zap_garbage: 0,
number_of_native_contexts: 1,
number_of_detached_contexts: 0
}
复制代码
在 node 15.14.0 版本中,打印如下
HeapStatistics: {
total_heap_size: 4284416,
total_heap_size_executable: 524288,
total_physical_size: 3122176,
total_available_size: 4342823264,
used_heap_size: 3384776,
heap_size_limit: 4345298944, // 4.04G
malloced_memory: 254120,
peak_malloced_memory: 90736,
does_zap_garbage: 0,
number_of_native_contexts: 1,
number_of_detached_contexts: 0
}
复制代码
也就是说,随着 Node 的发展,v8堆内存在不同版本默认设置是不一样的。
修改堆内存默认值
v8 的默认堆内存上限是可以修改的(两种方法):
- Node 启动时可以传递
--max-old-space-size
或--max-new-space-size
参数。 - 如果 Node8.x及以上的版本,还可以通过
NODE_OPTIONS
这个系统环境变量来配置export NODE_OPTIONS=--max_old_space_size=2048
这里说明一下 --max-old-space-size
和 --max-new-space-size
两个参数:
- –max-old-space-size: 设置堆内存老生代空间;
- –max-old-space-size: 设置堆内存新生代空间;(后面在内存分配和垃圾回收环节给大家详细解释)
上述参数在 v8 初始化时生效,一旦生效就不能动态改变了。
演示一下:
创建 memorySize.js:
const v8 = require('v8')
console.log('memoryUsage:',process.memoryUsage())
console.log('HeapStatistics:',v8.getHeapStatistics()) // 查询堆内存上限设置
复制代码
启动 node ,设置堆内存大小上限为 5000MB:
$ node
$ node --max-old-space-size=5000 memorySize.js
复制代码
打印如下:
v8.getHeapStatistics() 结果中的 heap_size_limit
5365510286 字节,即 5116 MB。
因为Node本身处于快速发展阶段,很多资料都与当前 Node 的表现存在着部分差异,为了严谨性,查询很多资料,肝了一夜!各位看官了解的可以留言沟通,不了解的看完之后点个赞吧?。
总结:
关于堆内存还有很多相关知识,本文主要介绍关于 v8 堆内存的查看、设置,及其在 Node 不同版本的表现。中间涉及的其它参数大家可以自行了解一下。欢迎大家一起交流。下一节为大家介绍内存的具体分配及回收模式。
参考资料:《深入浅出Nodejs》,社区实践