节点:fs write()不在循环内写入。为什么不?

用户名

我想创建一个写入流,并在数据输入时对其进行写入。但是,我能够创建该文件,但是没有写入任何内容。最终,该过程将耗尽内存。

我发现的问题是,我在循环内正在调用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轴显示堆内存使用量(红色)和增量时间(紫色):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();

慢-统计

随着内存使用率接近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();

慢-GC

大约4秒钟后,内存使用率达到80%,垃圾回收器放弃尝试Scavenge并被迫使用Mark-sweep(慢10倍以上)–有关更多详细信息,请参见本文


为了进行比较,以下是@MikeC的代码的可视化效果,它们drainwrite缓冲区变满等待

快速统计

快速-GC

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章