Node.js在大量逐位文件读取时内存不足

时间:2011-10-25 01:19:53

标签: memory node.js stream buffer

我正在尝试编写一些JS来读取文件并将其写入流中。这笔交易是文件非常大,所以我必须一点一点地读它。似乎我不应该耗尽内存,但我确实如此。这是代码:

var size = fs.statSync("tmpfile.tmp").size;

var fp = fs.openSync("tmpfile.tmp", "r");

for(var pos = 0; pos < size; pos += 50000){
    var buf = new Buffer(50000),
        len = fs.readSync(fp, buf, 0, 50000, (function(){
            console.log(pos);
            return pos;
        })());

    data_output.write(buf.toString("utf8", 0, len));

    delete buf;
}

data_output.end();

出于某种原因,它命中264900000,然后抛出FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory。我认为data_output.write()调用会强制它将数据写入data_output,然后将其从内存中丢弃,但我可能是错的。有些东西导致数据留在内存中,我不知道它会是什么。任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:3)

我有一个非常类似的问题。我在一个非常大的csv文件中读取10M行,并写出它的json等价物。我在Windows任务管理器中看到我的进程正在使用&gt; 2GB内存。最终我发现输出流可能比输入流慢,并且输出流缓冲了大量数据。我能够通过暂停每100次写入到流出的插入,并等待流出清空来解决这个问题。这为外流提供了赶上内流的时间。我不认为这对于这个讨论很重要,但是我使用'readline'来一次处理一行csv文件。

我还想到了如果不是将每一行写入流出,而是将100行连接在一起,然后将它们一起写入,这也改善了内存情况,并使操作更快。

最后,我发现只使用70M的内存就可以进行文件传输(csv - &gt; json)。

这是我的写函数的代码片段:

var write_counter = 0;
var out_string = "";
function myWrite(inStream, outStream, string, finalWrite) {
    out_string += string;
    write_counter++;
    if ((write_counter === 100) || (finalWrite)) {
        // pause the instream until the outstream clears
        inStream.pause();
        outStream.write(out_string, function () {
            inStream.resume();
        });
        write_counter = 0;
        out_string = "";
    }
}

答案 1 :(得分:2)

您应该使用管道,例如:

var fp = fs.createReadStream("tmpfile.tmp");
fp.pipe(data_output);

有关详细信息,请查看:http://nodejs.org/docs/v0.5.10/api/streams.html#stream.pipe

编辑:你的实现中的问题,顺便说一下,通过像这样的块来做,写缓冲区不会被刷新,你将在写入大部分内容之前读取整个文件退出。

答案 2 :(得分:1)

根据the documentation,如果字符串已被刷新,则data_output.write(...)将返回true,如果没有,则false将返回(由于内核缓冲区已满)。这是什么样的流?

另外,我(相当)确定这不是问题,但是:为什么要在每次循环迭代中分配一个新的Buffer?在循环之前初始化buf会不会更有意义吗?

答案 3 :(得分:0)

我不知道同步文件函数是如何实现的,但您是否考虑过使用异步函数?这将更有可能允许垃圾收集和i / o刷新发生。因此,不是for循环,而是触发上一次读取的回调函数中的下一次读取。

这些方面的内容(另请注意,根据其他评论,我正在重复使用缓冲区):

var buf = new Buffer(50000),
var pos = 0, bytesRead;  

function readNextChunk () {
    fs.read(fp, buf, 0, 50000, pos,
      function(err, bytesRead){
        if (err) {
          // handle error            
        }
        else {
          data_output.write(buf.toString("utf8", 0, bytesRead));
          pos += bytesRead;
          if (pos<size)
            readNextChunk();
        }
      });
}
readNextChunk();