nodejs 使用的V8 默认内存限制(x64 1.4g) 可以通过process.memoryUsage()证明吗?
发布于 3 个月前 作者 chaochao-zhang 669 次浏览 来自 问答

最近在读朴灵老师的《深入浅出》一书,其中提到了x64,老年代内存默认限制 x64 1.4g。并且使用一段代码演示了内存溢出(为了打印方便我稍作了改动)。

var showMem = function (times) {
    var mem = process.memoryUsage();
    var format = function (bytes) {
        return (bytes / 1024 / 1024).toFixed(2) + ' MB';
    };
    console.log('Process: heapTotal ' + format(mem.heapTotal) +
        ' heapUsed ' + format(mem.heapUsed) + ' rss ' + format(mem.rss));
    console.log(`--------------------------${times}---------------------------------`);
};
var useMem = function () {
    var size =  20*1024 * 1024;
    var arr = new Array(size);
    for (var i = 0; i < size; i++) {
        arr[i] = 0;
    }
    return arr;
};
var total = [];
for (var j = 0; j < 10000; j++) {
    showMem(j);
    total.push(useMem());
}
showMem();

正如预期,代码运行到内存需要占用1.4g的时候产生了内存溢出

Process: heapTotal 1286.92 MB heapUsed 1284.28 MB rss 1301.82 MB
--------------------------8---------------------------------
<--- Last few GCs --->
[23880:00000219B66A3AB0]     1055 ms: Mark-sweep 1284.0 (1290.9) -> 1283.9 (1290.9) MB, 119.6 / 0.0 ms  allocation failure GC in old space requested

我又写了一段代码,把一段字符重复写入文件。却没有产生同样的预期,代码如下:

const fs = require('fs');
async function writeFile(){
    const fd =  fs.openSync('a.log', 'a');
	const log = async (entry) =>{
		await fs.writeSync(fd, entry+'\r\n', null, 'utf8');
		await fs.fsyncSync(fd);
    }    
    for(let t = 0; t<10000 ; t++ ) {
        for(var i = 0x4E00;i<=0x9FBF;++i)
            log(String.fromCharCode(i));
    	showMem({times:t});
    }
}
const showMem = function ({times}) {
    const mem = process.memoryUsage();
    const format = function (bytes) {
        return (bytes / 1024 / 1024).toFixed(2) + ' MB';
    };	
    console.log('Process: heapTotal ' + format(mem.heapTotal) +
        ' heapUsed ' + format(mem.heapUsed) + ' rss ' + format(mem.rss));
	console.log(`-----------------------------${times}--------------------------------\r\n`)
}
writeFile();

代码在执行了200次后,发生了内存溢出,打印如下

Process: heapTotal 2736.84 MB heapUsed 2654.99 MB rss 2759.86 MB
-----------------------------199--------------------------------
<--- Last few GCs --->
[4648:00000184CB8B2380]   450417 ms: Mark-sweep 2660.1 (2751.3) -> 2660.0 (2717.8) MB, 2757.0 / 0.0 ms  last resort GC in old space requested
[4648:00000184CB8B2380]   453198 ms: Mark-sweep 2660.0 (2717.8) -> 2660.0 (2713.8) MB, 2779.5 / 0.0 ms  last resort GC in old space requested

可以看到,堆内存打印出来的是2.7g,这个使我很困惑,同样的环境,为什么我的代码显示可以占用更多的堆内存?而朴灵老师的代码可以证实1.4g堆内存?是我的写法有问题还是这种说法基于哪些上下文?(我的系统是 windows x64, 16g内存, node -v v8.11.4 ,node_options都是默认,没有设置堆内存) 如能提供帮助,不胜感激。

8 回复

node 版本相同么?

@waitingsong 谢谢回复,是同一台机器。使用cmd执行。

版本更新了 这个限制可以设置的

@chenjiyong 从哪个版本开始的

@chenjiyong 谢谢提示,我知道可以通过 --max-old-space-size=xxx 去设置堆内存,我只是想用代码证实一下默认的堆限制。但是我这段代码好像不受堆内存限制。 我找了一个阿里云服务器测试(CentOS 7.3 64位 4G).测试 第一段代码。是可以限制堆内存的。

[root@www demo]# node --max-old-space-size=400 outofmemory.js
Process: heapTotal 6.83 MB heapUsed 4.20 MB rss 19.05 MB
--------------------------0---------------------------------
Process: heapTotal 166.84 MB heapUsed 164.24 MB rss 180.63 MB
--------------------------1---------------------------------
Process: heapTotal 326.85 MB heapUsed 324.24 MB rss 340.71 MB
--------------------------2---------------------------------

<--- Last few GCs --->

[29310:0x260bfb0]      398 ms: Mark-sweep 323.9 (330.9) -> 323.9 (330.9) MB, 56.0 / 0.0 ms  allocation failure GC in old space requested

再测试我的代码,还是不受限制

[root@www demo]# node --max-old-space-size=500 wf.js
...
Process: heapTotal 704.34 MB heapUsed 667.80 MB rss 718.34 MB
-----------------------------49--------------------------------


<--- Last few GCs --->

[29431:0x2e6dfa0]    25144 ms: Mark-sweep 668.3 (713.3) -> 668.0 (717.8) MB, 669.4 / 0.0 ms  allocation failure GC in old space requested

从字面意思来讲,我应该是忽略了堆内存除了老年代内存,还包括新生代的内存。并且新生代的内存应该是按照老年代的内存比例分配的。我可以按照这个思路试一下。回头把结果贴过来。

自问自答了。结论应该就是新生代内存的问题。我的代码每次分配的内存比较小,所以新生代内存够分配。在内存不断增长后,就填满了新生代+老年代,所以占用了更多的内存,朴灵老师的代码直接分配到了老年代,所以只能占用老年代的内存。所以我的代码打印出内存总数和占用都更高。可以把两个都设置为固定的值进行测试。 (我还没有确切的答案为什么数组直接分配到老年代,因为我设置更大的新生代内存,朴灵老师的代码也没有分配到新生代,直接占用满老年代就溢出了)

(CentOS 7.3 x64 4G)
[root@www demo]#  node --max-old-space-size=100  --max_semi_space_size=100  wf.js
回到顶部