在以下代码中,我试图找出导致内存使用量显着增加的原因。
async function a () {
for (let i = 0; i < 10000000000; i++) {
await new Promise(resolve => {
if (i%100000 === 0) {
console.log(i)
console.log(process.memoryUsage())
}
resolve(i)
})
}
}
a()
在代码运行时,这种内存使用量跳跃会发生多次,并且总是在i达到一定数字时发生。
1700000
{ rss: 20135936, heapTotal: 9355264, heapUsed: 6003256, external: 8772 }
1800000
{ rss: 19836928, heapTotal: 9355264, heapUsed: 4490432, external: 8772 }
1900000
{ rss: 19316736, heapTotal: 9355264, heapUsed: 5039992, external: 8772 }
<-- Jump happens between here -->
2000000
{ rss: 19357696, heapTotal: 9355264, heapUsed: 5587808, external: 8772 }
2100000
{ rss: 23605248, heapTotal: 13549568, heapUsed: 6088208, external: 8772 }
<-- and here -->
2200000
{ rss: 23601152, heapTotal: 13549568, heapUsed: 6586000, external: 8772 }
2300000
{ rss: 23568384, heapTotal: 13549568, heapUsed: 7083112, external: 8772 }
2400000
{ rss: 30507008, heapTotal: 9437184, heapUsed: 4785896, external: 8252 }
2500000
{ rss: 30523392, heapTotal: 9437184, heapUsed: 4710912, external: 8252 }
<-- Jump happens between here -->
2600000
{ rss: 30539776, heapTotal: 9437184, heapUsed: 4636176, external: 8252 }
2700000
{ rss: 34742272, heapTotal: 13631488, heapUsed: 6606512, external: 8252 }
<-- and here -->
2800000
{ rss: 34750464, heapTotal: 13631488, heapUsed: 8571208, external: 8252 }
2900000
{ rss: 34758656, heapTotal: 13631488, heapUsed: 6412304, external: 8252 }
答案 0 :(得分:2)
在V8(Node.js使用的JS运行时)中,为堆预分配了一定数量的大小。这就是您看到的heapTotal
。当V8怀疑您将需要更多空间时,它将增加堆的总大小。
在您的示例代码中,发生的事情是在堆上分配了许多小对象。这反映在heapUsed
中,它是代码正在使用的实际内存量。当堆填满时,将执行一轮垃圾回收(GC),以释放空间。因此,如果您在增加heapUsed
的同时绘制i
,那么您会看到它不断上升,直到GC启动并且它又下降了。
事实上,这正是我非常长期以来所做的!
您可以清楚地看到,在GC启动之前,绝不允许堆变大。
要进一步验证这一点,如果我们使用node --expose_gc
async function run() {
for (let i = 0; i < 10000000000; i++) {
await new Promise(async resolve => {
if (i % 10000000 === 0) {
global.gc();
console.log(`${i}, ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}mb`);
}
resolve();
});
}
}
run();
您将在v7.9.0
上获得以下输出
0, 3.12mb
10000000, 2.77mb
20000000, 2.78mb
30000000, 2.78mb
40000000, 2.78mb
50000000, 2.78mb
60000000, 2.78mb
非常有趣的是,如果我们在不同版本的节点上运行测试!
如您所见,v8.2.0之前的版本和后续版本的node.js的内存配置文件存在巨大差异。如果我们去看看v8.3.0的change log,我们会明白为什么!
V8引擎已升级到版本6.0,该版本的性能概况已大大改变
这是V8的版本,其中包括Turbofan,该版本实现了节点之多,并且为GC提供了许多性能增强。
在Thorsten Lorenz的v8-perf回购中可以更深入地了解V8 GC的工作原理。