动态修改Node.js中的内存限制
背景
Node.js中默认会对可用内存做出限制,当使用的内存超出1.5GB的默认值时,会报内存溢出的异常。
对于这个问题,Node官网中有相关说明:--max-old-space-size
。
有两种方式可以修改Node的默认内存限制,分别是
1node --max-old-space-size=4096 index.js
和
1NODE_OPTIONS=--max-old-space-size=4096 node index.js
以webpack为例,大的项目中webpack内存溢出的可能性较大,可以用下面的命令将其可用内存扩大到4GB。
1# 所有平台 2node --max-old-space-size=4096 node_modules/webpack/bin/webpack.js --config webpack.config.js 3 4# 或者*nix 5NODE_OPTIONS=--max-old-space-size=4096 webpack --config webpack.config.js 6 7# Windows 8cross-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#!/usr/bin/env node 2process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '' 3if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) { 4 process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096` 5}
写一个脚本测试下:
1#!/usr/bin/env node 2const v8 = require('v8') 3process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '' 4if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) { 5 process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096` 6} 7 8console.log(Math.round(v8.getHeapStatistics().total_available_size / 1024 / 1024) + ' MB')
调节上面的--max-old-space-size=4096
中的4096,换成更小的数字,可以看出console.log
的结果是没有区别的,说明这个方法不可行。
猜测是在Node启动的时候,这个参数就已经被读取了,后面再动态修改已经没有作用。
启动子进程
按照上面的猜测,如果先改环境变量,再重新启动脚本,应该就能够生效。
所以我想到了使用子进程的方式,先在主进程中设置好环境变量,然后启动子进程,子进程就会自动继承主进程中的环境变量。
Node.js中有两种启动子进程的方式,spawn
和cluster
,此处不详细展开。
使用spawn
的话,有可能会需要考虑不同操作系统中的路径问题等边界情况,为了使用方便,这里我采用了cluster
的方式。
核心逻辑为:
1#!/usr/bin/env node 2const cluster = require('cluster') 3if (cluster.isMaster) { 4 // 如果是主进程,修改环境变量 5 process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '' 6 if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) { 7 process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096` 8 } 9 10 // 然后启动一个子进程 11 cluster.fork() 12} else { 13 // 如果是子进程,正式加载业务逻辑 14 process.exit(0) 15}
这样我们的测试代码就是:
1#!/usr/bin/env node 2 3const v8 = require('v8') 4const cluster = require('cluster') 5 6process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '' 7if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) { 8 process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096` 9} 10 11if (cluster.isMaster) { 12 process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '' 13 if (!process.env.NODE_OPTIONS.includes('--max-old-space-size=')) { 14 process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ` --max-old-space-size=4096` 15 } 16 cluster.fork() 17} else { 18 console.log(Math.round(v8.getHeapStatistics().total_available_size / 1024 / 1024) + ' MB') 19 process.exit(0) 20} 21
试着修改一下代码中的4096,换成其它的值,可以看出console.log
的数字发生了变化,说明动态调整内存限制生效了。
总结
可以使用子进程的方式,动态调整Node.js命令行工具的可用内存。先修改NODE_OPTIONS的值,再使用cluster
启动新的子进程即可。