我想创建一个写入流,并在数据输入时对其进行写入。但是,我能够创建该文件,但是没有写入任何内容。最终,该过程将耗尽内存。
我发现的问题是,我在循环内正在调用write()。
这是一个简单的例子:
'use strict'
var fs = require('fs');
var wstream = fs.createWriteStream('myOutput.txt');
for (var i = 0; i < 10000000000; i++) {
wstream.write(i+'\n');
}
console.log('End!')
wstream.end();
什么都没写,甚至没有打招呼。但为什么?如何在循环中写入文件?
为了补充@MikeC的出色答案,以下是当前文档(v8.4.0)中的一些相关详细信息writable.write()
:
如果
false
返回,则应停止进一步的尝试将数据写入流的操作,直到'drain'
发出该事件为止。当流不耗尽时,对
write()
will的调用将缓冲chunk
,然后返回false
。一旦所有当前缓冲的块耗尽(由操作系统接受传递),'drain'
将发出事件。建议一旦write()
返回false
,就不再写入任何块,直到'drain'
发出事件为止。在write()
允许不排水的流上进行调用时,Node.js将缓冲所有已写入的块,直到出现最大内存使用量为止,此时它将无条件中止。即使在中止之前,高内存使用率也会导致较差的垃圾收集器性能和高RSS(即使不再需要内存后,通常也不会将其释放回系统)。
在任何情况下,如果数据缓冲区已超过
highWaterMark
或写队列当前处于繁忙状态,.write()
将返回false
。当
false
返回值,背压系统踢英寸清空数据缓冲区后,
.drain()
将发出一个事件并恢复传入的数据流。队列完成后,背压将允许再次发送数据。正在使用的内存空间将释放自身并为下一批数据做准备。
+-------------------+ +=================+
| Writable Stream +---------> .write(chunk) |
+-------------------+ +=======+=========+
|
+------------------v---------+
+-> if (!chunk) | Is this chunk too big? |
| emit .end(); | Is the queue busy? |
+-> else +-------+----------------+---+
| emit .write(); | |
^ +--v---+ +---v---+
^-----------------------------------< No | | Yes |
+------+ +---v---+
|
emit .pause(); +=================+ |
^-----------------------+ return false; <-----+---+
+=================+ |
|
when queue is empty +============+ |
^-----------------------< Buffering | |
| |============| |
+> emit .drain(); | ^Buffer^ | |
+> emit .resume(); +------------+ |
| ^Buffer^ | |
+------------+ add chunk to queue |
| <---^---------------------<
+============+
以下是一些可视化效果(通过使用,以512MB的V8堆内存大小运行脚本--max-old-space-size=512
)。
此可视化效果显示每10,000个步骤(X轴显示)的堆内存使用量(红色)和增量时间(紫色):i
i
'use strict'
var fs = require('fs');
var wstream = fs.createWriteStream('myOutput.txt');
var latestTime = (new Date()).getTime();
var currentTime;
for (var i = 0; i < 10000000000; i++) {
wstream.write(i+'\n');
if (i % 10000 === 0) {
currentTime = (new Date()).getTime();
console.log([ // Output CSV data for visualisation
i,
(currentTime - latestTime) / 5,
process.memoryUsage().heapUsed / (1024 * 1024)
].join(','));
latestTime = currentTime;
}
}
console.log('End!')
wstream.end();
随着内存使用率接近512MB的最大限制,脚本运行的速度越来越慢,直到达到限制时它最终崩溃。
该可视化使用v8.setFlagsFromString()
with--trace_gc
来显示每个垃圾回收的当前内存使用量(红色)和执行时间(紫色)(X轴以秒为单位显示总经过时间):
'use strict'
var fs = require('fs');
var v8 = require('v8');
var wstream = fs.createWriteStream('myOutput.txt');
v8.setFlagsFromString('--trace_gc');
for (var i = 0; i < 10000000000; i++) {
wstream.write(i+'\n');
}
console.log('End!')
wstream.end();
大约4秒钟后,内存使用率达到80%,垃圾回收器放弃尝试Scavenge
并被迫使用Mark-sweep
(慢10倍以上)–有关更多详细信息,请参见本文。
为了进行比较,以下是@MikeC的代码的可视化效果,它们drain
在write
缓冲区变满时等待:
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句