鱼肚的博客

发布于: 2020-11-28作者: 鱼肚最后更新: 2020-11-29

动态修改Node.js中的内存限制

背景

Node.js中默认会对可用内存做出限制,当使用的内存超出1.5GB的默认值时,会报内存溢出的异常。

对于这个问题,Node官网中有相关说明:--max-old-space-size

有两种方式可以修改Node的默认内存限制,分别是

node --max-old-space-size=4096 index.js

NODE_OPTIONS=--max-old-space-size=4096 node index.js

以webpack为例,大的项目中webpack内存溢出的可能性较大,可以用下面的命令将其可用内存扩大到4GB。

1
2
3
4
5
6
7
8
# 所有平台
node --max-old-space-size=4096 node_modules/webpack/bin/webpack.js --config webpack.config.js

# 或者*nix
NODE_OPTIONS=--max-old-space-size=4096 webpack --config webpack.config.js

# Windows
cross-env NODE_OPTIONS=--max-old-space-size=4096 webpack --config webpack.config.js

相对来说,NODE_OPTIONS的方式使用起来更简单一些,不过它在windows上会需要有cross-env等工具作为前缀。

这两种方式都需要修改启动命令,那么有没有不需要修改启动命令的方式呢?

动态调整

有些命令行工具本身就比较容易出现内存溢出的问题,所以会希望在程序的内部动态调整可用内存,避免使用方每个地方都要重新配置一次。

从上面的NODE_OPTIONS使用方式中可以看出,只需要修改NODE_OPTIONS环境变量,就能实现修改内存限制的目的。那么能不能通过修改process.env.NODE_OPTIONS的方式,实现这个目的呢?

直接修改环境变量

示例代码:

1
2
3
4
5
#!/usr/bin/env node
process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || ''
if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) {
  process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096`
}

写一个无限循环的脚本测试下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env node
process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || ''
if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) {
  process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096`
}

(function infiniteConsume () {
  const arr = []
  while (true) {
    arr.push(Object.assign({}, {v: `${Math.random()}`.repeat(1000000)}))
    console.log(arr.length)
  }
})()

调节上面的--max-old-space-size=4096中的4096,换成更小的数字,可以看出console.log的结果是没有区别的,说明这个方法不可行。

猜测是在Node启动的时候,这个参数就已经被读取了,后面再动态修改已经没有作用。

启动子进程

按照上面的猜测,如果先改环境变量,再重新启动脚本,应该就能够生效。

所以我想到了使用子进程的方式,先在主进程中设置好环境变量,然后启动子进程,子进程就会自动继承主进程中的环境变量。

Node.js中有两种启动子进程的方式,spawncluster,此处不详细展开。

使用spawn的话,有可能会需要考虑不同操作系统中的路径问题等边界情况,为了使用方便,这里我采用了cluster的方式。

核心逻辑为:

1
2
3
4
5
6
7
8
9
10
11
12
13
const cluster = require('cluster')
if (cluster.isMaster) {
  // 如果是主进程,修改环境变量
  process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || ''
  if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) {
    process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096`
  }
  
  // 然后启动一个子进程
  cluster.fork()
} else {
  // 如果是子进程,正式加载业务逻辑
}

这样我们的测试代码就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const cluster = require('cluster')
if (cluster.isMaster) {
  process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || ''
  if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) {
    process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096`
  }
  cluster.fork()
} else {
  (function infiniteConsume () {
    const arr = []
    while (true) {
      arr.push(Object.assign({}, {v: `${Math.random()}`.repeat(1000000)}))
      console.log(arr.length)
    }
  })()
}

试着修改一下代码中的4096,换成更小的值,可以看出console.log的数字发生了变化,说明动态调整内存限制生效了。

总结

可以使用子进程的方式,动态调整Node.js命令行工具的可用内存。先修改NODE_OPTIONS的值,再使用cluster启动新的子进程即可。



0条评论