nodejs中的console.log循环

时间:2015-10-22 14:26:01

标签: node.js 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可以做什么可能会导致内存不足错误?

3 个答案:

答案 0 :(得分:5)

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

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

但我测试了这个:

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

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

我也测试了这个:

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,直到它崩溃(因为没有更多的RAM可用)。文件stdout.log始终为空。

最后,我测试了这个:

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

答案 1 :(得分:3)

在nodejs存储库上打开问题后,我得到了following answer

  

预期的行为: console.log是异步的,在事件循环的下一个滴答之前,无法回收与每个调用关联的内存。在你的例子中,由于无限循环,下一个tick不会发生。如果您将示例重写为回调驱动方法,它将一直运行:

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

答案 2 :(得分:1)

https://github.com/nodejs/node/issues/11568

的讨论中查看更多详情

此外,正如文档中所描述的那样,当stdout重定向到文件时,控制台是同步。但在这种情况下,所描述的行为仍然存在。

console.log()输出重定向到文件的无限循环不会崩溃Node v4.x但会崩溃Node v6。 这似乎是v6中引入的一个bug。

作为临时解决方法,可以使用console-sync补丁。