在nodejs中的console.log循环

我的MCVE是以下

var i = 0; for(;;) console.log(i++) 

当我这样做,在某个特定的时刻,我的nodejs只是停止打印东西,给我一个看起来像这样的输出

 [...] 684665 684666 684667 

然后,我得到这个:

 <--- Last few GCs ---> 69097 ms: Scavenge 1397.2 (1456.7) -> 1397.2 (1456.7) MB, 0.8 / 0 ms (+ 1.7 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep]. 70462 ms: Mark-sweep 1397.2 (1456.7) -> 1396.0 (1456.7) MB, 1364.9 / 0 ms (+ 2.8 ms in 2 steps since start of marking, biggest step 1.7 ms) [last resort gc]. 71833 ms: Mark-sweep 1396.0 (1456.7) -> 1397.1 (1456.7) MB, 1370.2 / 0 ms [last resort gc]. <--- JS stacktrace ---> ==== JS stack trace ========================================= Security context: 0xcdf79d37399 <JS Object> 1: formatPrimitive(aka formatPrimitive) [util.js:~411] [pc=0x634d9f4113f] (this=0xcdf79d04131 <undefined>,ctx=0x17b18f4d561 <an Object with map 0x32fd25043ef9>,value=16248021) 2: formatValue(aka formatValue) [util.js:223] [pc=0x634d9f1fdbb] (this=0xcdf79d04131 <undefined>,ctx=0x17b18f4d561 <an Object with map 0x32fd25043ef9>,value=16248021,recurseTimes=2) 3: inspect(aka inspect) [uti... FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory [1] 19446 abort (core dumped) node 

我想知道,什么console.log可以导致内存不足的错误?

根据这个讨论: https : //groups.google.com/forum/#!topic/ nodejs/ KtONbpVV68U

每次调用console.log (或其他日志logging方法)时,控制台对象都会分配一些内存。 这个内存将在下一个tick时被垃圾收集器释放。 但是,如果你有一个太大的循环,下一个滴答声永远不会来。

但是我testing了这个:

 setInterval(function() { for (var i = 0; i < 100000; ++i) { console.log(i); } }, 10000) 

根据谷歌小组讨论,在每个时间间隔之间,NodeJS垃圾收集器应该通过console.log释放分配的内存。 但事实并非如此。 每次循环运行时,我的进程需要更多的RAM。

我也testing了这个:

 var fs = require('fs') var output = fs.createWriteStream('./stdout.log'); var errorOutput = fs.createWriteStream('./stderr.log'); var logger = new console.Console(output, errorOutput); var i = 0; for (;;) { logger.log(i++); } 

行为是一样的。 这个过程需要更多的内存,直到它崩溃(因为更多的RAM可用)。 而stdout.log文件总是空的。

最后,我testing了这个:

 var fs = require('fs') var output = fs.createWriteStream('./stdout.log'); var errorOutput = fs.createWriteStream('./stderr.log'); var logger = new console.Console(output, errorOutput); setInterval(function() { for (var i = 0; i < 100000; i++) { logger.log(i); } }, 5000) 

这个例子很有趣。 因为在每个时间间隔之间, stdout.log被写入(行被追加到文件中)并且进程回收的RAM不会增长。 垃圾收集器在每个时间间隔之间进行工作。

我认为控制台对象不能很好地处理缓冲区。 但它仍然很奇怪。 如果你使用标准输出(只有一个console.log ),看起来控制台对象保存在内存中,打印到目前为止。 而且从来没有净化。 如果你使用文件输出,一切正常(除非你写在无限循环,当然)。

也许是因为NodeJS版本,我正在使用NodeJS 0.12.7

在nodejs存储库上打开一个问题后,我得到了以下答案 :

这是预期的行为: console.log是asynchronous的 ,与每个调用相关的内存不能被回收,直到事件循环的下一个滴答声。 在你的例子中,由于无限循环的缘故,下一个勾选不会发生。 如果您将您的示例重写为callback驱动的方法,则它会一直运行:

  let i = 0; const next = () => process.stdout.write(`${i++}\n`, next); next(); 

https://github.com/nodejs/node/issues/11568查看更多的细节讨&#x8BBA;

另外,正如文档中所描述的,当stdoutredirect到文件时,控制台是同步的 。 但描述的行为仍然在这种情况下再现。

console.log()输出redirect到文件的无限循环不会使Node v4.x崩溃,而会导致Node v6崩溃。 这看起来像是在v6中引入的一个bug。

作为临时解决方法,可以使用控制台同步修补程序。