【发布时间】:2018-10-25 17:37:51
【问题描述】:
这段代码
const file = require("fs").createWriteStream("./test.dat");
for(var i = 0; i < 1e7; i++){
file.write("a");
}
运行大约 30 秒后给出此错误消息
<--- Last few GCs --->
[47234:0x103001400] 27539 ms: Mark-sweep 1406.1 (1458.4) -> 1406.1 (1458.4) MB, 2641.4 / 0.0 ms allocation failure GC in old space requested
[47234:0x103001400] 29526 ms: Mark-sweep 1406.1 (1458.4) -> 1406.1 (1438.9) MB, 1986.8 / 0.0 ms last resort GC in old spacerequested
[47234:0x103001400] 32154 ms: Mark-sweep 1406.1 (1438.9) -> 1406.1 (1438.9) MB, 2628.3 / 0.0 ms last resort GC in old spacerequested
<--- JS stacktrace --->
==== JS stack trace =========================================
Security context: 0x30f4a8e25ee1 <JSObject>
1: /* anonymous */ [/Users/matthewschupack/dev/streamTests/1/write.js:~1] [pc=0x270efe213894](this=0x30f4e07ed2f1 <Object map = 0x30f4ede823b9>,exports=0x30f4e07ed2f1 <Object map = 0x30f4ede823b9>,require=0x30f4e07ed2a9 <JSFunction require (sfi = 0x30f493b410f1)>,module=0x30f4e07ed221 <Module map = 0x30f4edec1601>,__filename=0x30f493b47221 <String[49]: /Users/matthewschupack/dev/streamTests/...
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
1: node::Abort() [/usr/local/bin/node]
2: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [/usr/local/bin/node]
3: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [/usr/local/bin/node]
4: v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [/usr/local/bin/node]
5: v8::internal::Runtime_AllocateInTargetSpace(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/local/bin/node]
6: 0x270efe08463d
7: 0x270efe213894
8: 0x270efe174048
[1] 47234 abort node write.js
而这段代码
const file = require("fs").createWriteStream("./test.dat");
for(var i = 0; i < 1e6; i++){
file.write("aaaaaaaaaa");//ten a's
}
几乎可以立即完美运行并生成一个 10MB 的文件。据我了解,流的意义在于两个版本应该在大约相同的时间内运行,因为数据是相同的。即使每次迭代将as 的数量增加到 100 或 1000 也几乎不会增加运行时间,并且写入 1GB 文件也没有任何问题。在 1e6 次迭代中每次迭代写入一个字符也可以正常工作。
这是怎么回事?
【问题讨论】:
-
疯狂猜测。可能是一次写入一个字符会导致更多的内存分配来调整流缓冲区的大小,但运行 1e7 循环永远不会让 GC 有机会运行或写入有机会得到处理。
-
我不知道为什么
1e7将其置于边缘,但您应该能够通过使用drain事件来避免OOM 以尊重背压。file.write(...)返回false如果您需要在下一次写入之前等待。这里的文档中有一个示例:nodejs.org/api/… -
或者你可以从一个可读流到写流,它会为你处理背压
-
它们是相同的,只是一个使用更多的内存会导致错误。流的重点是允许缓冲 I/O,您可以在缓冲块中读入/读出以避免此类内存溢出问题。
标签: node.js v8 node-streams