My MCVE is the following
我的MCVE如下
var i = 0;
for(;;)
console.log(i++)
When I do this, at a certain moment, my nodejs
just stops printing stuff, giving me an output that looks like this
当我这样做时,在某个时刻,我的nodejs只是停止打印东西,给我一个看起来像这样的输出
[...]
684665
684666
684667
And then, I got this :
然后,我得到了这个:
<--- 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
I was wondering, what can console.log
do that could lead to an out of memory error ?
我想知道,console.log可以做什么导致内存不足错误?
3 个解决方案
#1
5
According to this discussion : https://groups.google.com/forum/#!topic/nodejs/KtONbpVV68U
根据此讨论:https://groups.google.com/forum/#!topic / nodejs / KtONbpVV68U
Every time you call console.log
(or an other logging method), the console object will allocate some memory. This memory will be free by the garbage collector on its next tick. But if you have a too big loop, the next tick never come.
每次调用console.log(或其他日志记录方法)时,控制台对象都会分配一些内存。垃圾收集器在下一个滴答时将释放该内存。但是如果你有一个太大的循环,那么下一个勾号永远不会到来。
But i tested this :
但我测试了这个:
setInterval(function() {
for (var i = 0; i < 100000; ++i) {
console.log(i);
}
}, 10000)
According to the google group discussion, between each interval, NodeJS garbage collector should free the allocated memory by the console.log. But it is not. Every time the loop is running, my process take more RAM.
根据谷歌小组讨论,在每个间隔之间,NodeJS垃圾收集器应该通过console.log释放分配的内存。但事实并非如此。每次循环运行时,我的进程占用更多内存。
I also tested this :
我也测试了这个:
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++);
}
The behavior is the same. The process take more and more RAM until it crash (because nore more RAM is available). And the file stdout.log
is always empty.
行为是一样的。这个过程需要越来越多的RAM,直到它崩溃(因为没有更多的RAM可用)。并且文件stdout.log始终为空。
Finally, i tested this :
最后,我测试了这个:
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)
This example is interesting. Because between each interval, stdout.log
is written (the lines are appended to the file) and the RAM reclaimed by the process doesn't grow. Between each interval the garbage collector do its works.
这个例子很有趣。因为在每个间隔之间写入了stdout.log(行被附加到文件中),并且进程回收的RAM不会增长。垃圾收集器在每个间隔之间完成其工作。
I think the console object does not handle well the buffer. But it is still weird. If you use the standard output (with just a console.log
), it's look the console object keep in memory everthing printed so far. And it is never cleanse. And if you use a file ouput, everything works fine (except if you write it in an infinity loop, of course).
我认为控制台对象不能很好地处理缓冲区。但它仍然很奇怪。如果你使用标准输出(只有一个console.log),它看起来控制台对象保持在目前为止打印的内存中。它永远不会被清洗。如果你使用文件输出,一切正常(当然,除非你在无限循环中写它)。
Maybe it's because of the NodeJS version, i'm working with a NodeJS 0.12.7
也许是因为NodeJS版本,我正在使用NodeJS 0.12.7
#2
2
After opening an issue on the nodejs repository, I got the following answer :
在nodejs存储库上打开一个问题后,我得到了以下答案:
It's expected behavior: console.log is asynchronous, the memory associated with each call cannot be reclaimed until the next tick of the event loop. In your example that next tick never happens because of the infinite loop. If you rewrite your example to a callback-driven approach, it keeps running forever:
这是预期的行为:console.log是异步的,在事件循环的下一个滴答之前,无法回收与每个调用关联的内存。在你的例子中,由于无限循环,下一个tick不会发生。如果您将示例重写为回调驱动方法,它将一直运行:
let i = 0; const next = () => process.stdout.write(`${i++}\n`, next); next();
#3
1
See more details in discussion at https://github.com/nodejs/node/issues/11568
有关详细信息,请参阅https://github.com/nodejs/node/issues/11568
Also, as descibed in documentation the console is synchronous when stdout redirected to a file. But described behavior still also reproduced in this case.
此外,正如文档中所描述的那样,当stdout重定向到文件时,控制台是同步的。但在这种情况下仍然会再现所描述的行为。
An infinite loop with console.log()
output redirected to a file does not crash Node v4.x but crash Node v6. This is seems like a bug introduced in v6.
将console.log()输出重定向到文件的无限循环不会崩溃Node v4.x但会崩溃Node v6。这似乎是v6中引入的错误。
As a temporary workaround it is possible to use console-sync patch.
作为临时解决方法,可以使用控制台同步补丁。
#1
5
According to this discussion : https://groups.google.com/forum/#!topic/nodejs/KtONbpVV68U
根据此讨论:https://groups.google.com/forum/#!topic / nodejs / KtONbpVV68U
Every time you call console.log
(or an other logging method), the console object will allocate some memory. This memory will be free by the garbage collector on its next tick. But if you have a too big loop, the next tick never come.
每次调用console.log(或其他日志记录方法)时,控制台对象都会分配一些内存。垃圾收集器在下一个滴答时将释放该内存。但是如果你有一个太大的循环,那么下一个勾号永远不会到来。
But i tested this :
但我测试了这个:
setInterval(function() {
for (var i = 0; i < 100000; ++i) {
console.log(i);
}
}, 10000)
According to the google group discussion, between each interval, NodeJS garbage collector should free the allocated memory by the console.log. But it is not. Every time the loop is running, my process take more RAM.
根据谷歌小组讨论,在每个间隔之间,NodeJS垃圾收集器应该通过console.log释放分配的内存。但事实并非如此。每次循环运行时,我的进程占用更多内存。
I also tested this :
我也测试了这个:
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++);
}
The behavior is the same. The process take more and more RAM until it crash (because nore more RAM is available). And the file stdout.log
is always empty.
行为是一样的。这个过程需要越来越多的RAM,直到它崩溃(因为没有更多的RAM可用)。并且文件stdout.log始终为空。
Finally, i tested this :
最后,我测试了这个:
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)
This example is interesting. Because between each interval, stdout.log
is written (the lines are appended to the file) and the RAM reclaimed by the process doesn't grow. Between each interval the garbage collector do its works.
这个例子很有趣。因为在每个间隔之间写入了stdout.log(行被附加到文件中),并且进程回收的RAM不会增长。垃圾收集器在每个间隔之间完成其工作。
I think the console object does not handle well the buffer. But it is still weird. If you use the standard output (with just a console.log
), it's look the console object keep in memory everthing printed so far. And it is never cleanse. And if you use a file ouput, everything works fine (except if you write it in an infinity loop, of course).
我认为控制台对象不能很好地处理缓冲区。但它仍然很奇怪。如果你使用标准输出(只有一个console.log),它看起来控制台对象保持在目前为止打印的内存中。它永远不会被清洗。如果你使用文件输出,一切正常(当然,除非你在无限循环中写它)。
Maybe it's because of the NodeJS version, i'm working with a NodeJS 0.12.7
也许是因为NodeJS版本,我正在使用NodeJS 0.12.7
#2
2
After opening an issue on the nodejs repository, I got the following answer :
在nodejs存储库上打开一个问题后,我得到了以下答案:
It's expected behavior: console.log is asynchronous, the memory associated with each call cannot be reclaimed until the next tick of the event loop. In your example that next tick never happens because of the infinite loop. If you rewrite your example to a callback-driven approach, it keeps running forever:
这是预期的行为:console.log是异步的,在事件循环的下一个滴答之前,无法回收与每个调用关联的内存。在你的例子中,由于无限循环,下一个tick不会发生。如果您将示例重写为回调驱动方法,它将一直运行:
let i = 0; const next = () => process.stdout.write(`${i++}\n`, next); next();
#3
1
See more details in discussion at https://github.com/nodejs/node/issues/11568
有关详细信息,请参阅https://github.com/nodejs/node/issues/11568
Also, as descibed in documentation the console is synchronous when stdout redirected to a file. But described behavior still also reproduced in this case.
此外,正如文档中所描述的那样,当stdout重定向到文件时,控制台是同步的。但在这种情况下仍然会再现所描述的行为。
An infinite loop with console.log()
output redirected to a file does not crash Node v4.x but crash Node v6. This is seems like a bug introduced in v6.
将console.log()输出重定向到文件的无限循环不会崩溃Node v4.x但会崩溃Node v6。这似乎是v6中引入的错误。
As a temporary workaround it is possible to use console-sync patch.
作为临时解决方法,可以使用控制台同步补丁。