Skip to content

V8 内存限制

Node 与 V8

Node在JavaScript的执行上直接受益于V8,可以随着V8的升级就能享受到更好的性能或新的语言特性(如ES5和ES6)等,同时也受到V8的一些限制。

V8 的内存限制

在一般的后端开发语言中,在基本的内存使用上没有什么限制,然而在Node中通过JavaScript使用内存时就会发现只能使用部分内存(64位系统下约为1.4 GB,32位系统下约为0.7 GB)。

在这样的限制下,将会导致Node无法直接操作大内存对象,比如无法将一个2 GB的文件读入内存中进行字符串分析处理,即使物理内存有32 GB。这样在单个Node进程的情况下,计算机的内存资源无法得到充足的使用。

V8 的对象分配

在V8中,所有的JavaScript对象都是通过堆来进行分配的。Node提供了V8中内存使用量的查看方式,执行下面的代码,将得到输出的内存信息:

javascript
$ node
Welcome to Node.js v16.18.1.
Type ".help" for more information.
> process.memoryUsage();
{
  rss: 27639808,
  heapTotal: 5804032,
  heapUsed: 4260752,
  external: 991939,
  arrayBuffers: 26876
}

在上述代码中,在memoryUsage()方法返回的5个属性中,heapTotalheapUsed是V8的堆内存使用情况。

rss: "Resident Set Size",是进程在物理内存中占用的空间量,包括代码段、堆和栈的大小。

heapTotal是已申请到的堆内存,heapUsed是当前使用的量。

external: 外部内存使用量,表示绑定到该进程的共享库或资源的内存使用量。

arrayBuffers: 用于存储原始二进制数据的数组缓冲区的数量。用于处理大量数据,例如在处理图像、音频、视频或其他二进制文件时。

当我们在代码中声明变量并赋值时,所使用对象的内存就分配在堆中。如果已申请的堆空闲内存不够分配新的对象,将继续申请堆内存,直到堆的大小超过V8的限制为止。

至于V8为何要限制堆的大小,表层原因为V8最初为浏览器而设计,不太可能遇到用大量内存的场景。对于网页来说,V8的限制值已经绰绰有余。深层原因是V8的垃圾回收机制的限制。按官方的说法,以1.5 GB的垃圾回收堆内存为例,V8做一次小的垃圾回收需要50毫秒以上,做一次非增量式的垃圾回收甚至要1秒以上。这是垃圾回收中引起JavaScript线程暂停执行的时间,在这样的时间花销下,应用的性能和响应能力都会直线下降。

这个限制也不是不能打开,V8依然提供了选项让我们使用更多的内存。Node在启动时可以传递--max-old-space-size--max-new-space-size来调整内存限制的大小

sh
node --max-old-space-size=1700 test.js // 单位为MB
// 或者
node --max-new-space-size=1024 test.js // 单位为KB

上述参数在V8初始化时生效,一旦生效就不能再动态改变。如果遇到Node无法分配足够内存给JavaScript对象的情况,可以用这个办法来放宽V8默认的内存限制,避免在执行过程中稍微多用了一些内存就轻易崩溃。