use*_*603 5 javascript fs node.js
我想创建一个写入流并在我的数据进入时写入它.但是,我能够创建该文件,但没有写入任何内容.最终,该过程耗尽内存.
我发现问题是我在循环中调用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();
Run Code Online (Sandbox Code Playgroud)
什么都没有写,甚至没有你好.但为什么?如何在循环内写入文件?
为了补充@MikeC 的出色回答,以下是当前文档 (v8.4.0) 中的一些相关细节writable.write():
如果
false返回,则应停止进一步尝试将数据写入流,直到'drain'发出事件。当流没有排空时,调用
write()将缓冲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 |
| <---^---------------------<
+============+
Run Code Online (Sandbox Code Playgroud)
以下是一些可视化效果(通过使用 以 512MB 的 V8 堆内存大小运行脚本--max-old-space-size=512)。
此可视化显示了每 10,000 步(X 轴显示)的堆内存使用情况(红色)和增量时间(紫色):ii
'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();
Run Code Online (Sandbox Code Playgroud)
随着内存使用量接近 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();
Run Code Online (Sandbox Code Playgroud)
大约 4 秒后内存使用率达到 80%,垃圾收集器放弃尝试Scavenge并被迫使用Mark-sweep(慢了 10 倍以上)——更多细节请参阅这篇文章。
为了进行比较,以下是 @MikeC 代码的相同可视化,它drain在write缓冲区变满时等待:
问题是你永远不会给它一个耗尽缓冲区的机会.最终这个缓冲区已满,你的内存不足.
WriteStream.write返回一个布尔值,指示数据是否已成功写入磁盘.如果未成功写入数据,则应等待drain事件,这表示缓冲区已耗尽.
这里是写你的代码,它利用的返回值的一种方法write和drain事件:
'use strict'
var fs = require('fs');
var wstream = fs.createWriteStream('myOutput.txt');
function writeToStream(i) {
for (; i < 10000000000; i++) {
if (!wstream.write(i + '\n')) {
// Wait for it to drain then start writing data from where we left off
wstream.once('drain', function() {
writeToStream(i + 1);
});
return;
}
}
console.log('End!')
wstream.end();
}
writeToStream(0);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1828 次 |
| 最近记录: |