节点进程内存不足

node process out of memory

我想了解为什么一个简单的节点进程 运行 内存不足。

出于好玩,我尝试了console.log 1000万次,一段时间后出现错误

<--- Last few GCs --->

  215529 ms: Scavenge 1485.5 (1532.7) -> 1485.5 (1532.7) MB, 43.9 / 0 ms (+ 1.8 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep].
  216476 ms: Mark-sweep 1485.5 (1532.7) -> 1435.3 (1482.5) MB, 947.9 / 0 ms (+ 2.9 ms in 2 steps since start of marking, biggest step 1.8 ms) [last resort gc].
  217537 ms: Mark-sweep 1435.3 (1482.5) -> 1435.3 (1482.5) MB, 1060.2 / 0 ms [last resort gc].


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x76575fe3ac1 <JS Object>
    1: _writeGeneric [net.js:645] [pc=0x1f0bf7164345] (this=0x6175737bfe9 <a WriteStream with map 0x3dda56221639>,writev=0x76575f04299 <false>,data=0x3d61a01691b9 <String[18]\: madan chutiya hai\n>,encoding=0x76575fed8d9 <String[4]: utf8>,cb=0x61757359889 <JS Function WritableState.onwrite (SharedFunctionInfo 0x61757359411)>)
    2: writeOrBuffer(aka writeOrBuffer) [_stream_writable.js:~255] [pc...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Abort trap: 6

我正在执行的简单程序是

for (var index = 0; index < 10000000; index++) {
        console.log('hello world');
}

内存不足错误背后的原因是什么?

更新:

Ben Noordhuis 在此处谈论 console.log 内存:https://groups.google.com/forum/#!topic/nodejs/KtONbpVV68U

”每个console.log()语句分配一些内存 回收直到事件循环的下一个滴答声。"

所以这就是为什么你 运行 内存不足,即使你不一定要创建一堆对象。

原文:

根据@Vasil 的评论,我认为他是对的。事件循环被阻塞,无法 运行 垃圾回收。 FWIW 我没有任何问题 运行 在节点 v6.2.2 上使用您的示例代码。但是如果你玩弄分配的内存,我可以通过添加这些标志来重现你的问题:--max-executable-size=192 --max-old-space-size=256 --max-semi-space-size=2。为了证明 process.nextTick() 理论,我们可以将您的测试脚本修改为如下内容:

// test.js
var BATCH_LIMIT = 250000;
var TEN_MILLION = 10000000;

var useNextTick = true; // switch to false to reproduce out of memory error

function log(start, end) {
  for(var index = start || 0; index<end; index++) {
    console.log('hello world', index);
    // if useNextTick is enabled we will call next tick every 250,000 iterations so GC can run
    if(useNextTick && index && index % BATCH_LIMIT === 0) {
      console.log('NEXT TICK');
      // bind the next start and end points to the log function that process.nextTick will call
      return process.nextTick(log.bind(this, index+1, index + (TEN_MILLION - index)));
    }
  }
}

// kick it off with 10,000,000 iterations
log(0, TEN_MILLION);

并且运行它的内存有限:

node --max-executable-size=192 --max-old-space-size=256 --max-semi-space-size=2 test.js

可能有一种更简洁的编码方式,但希望它能让您了解实际发生的事情以及为什么不阻塞事件循环很重要。

您也可以像这样使用 --expose-gc Node.js 标志:

https://simonmcmanus.wordpress.com/2013/01/03/forcing-garbage-collection-with-node-js-and-v8/

然后使用

global.gc();

在循环内;尝试结合 process.nextTick()