Node.js не хватает памяти для большого побитового чтения файла - PullRequest
2 голосов
/ 25 октября 2011

Я пытаюсь написать немного 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 ]

3 голосов
/ 14 декабря 2016

У меня была очень похожая проблема. Я читал в очень большом CSV-файле с 10M строк и выписывал его эквивалент JSON. В диспетчере задач Windows я увидел, что мой процесс использует> 2 ГБ памяти. В конце концов я понял, что выходной поток, вероятно, медленнее, чем входной поток, и что этот поток буферизировал огромное количество данных. Я смог это исправить, останавливая инстрим каждые 100 записей в аутстрим и ожидая, пока аутсайд опустеет. Это дает время для опережающего действия, чтобы догнать его. Я не думаю, что это имеет значение для обсуждения, но я использовал readline для обработки csv-файла по одной строке за раз.

Я также выяснил, что если вместо записи каждой строки в выходной поток я объединю около 100 или около того строк вместе, а затем записываю их вместе, это также улучшило ситуацию с памятью и ускорило работу.

В конце концов, я обнаружил, что могу передавать файлы (csv -> json), используя только 70 МБ памяти.

Вот фрагмент кода для моей функции записи:

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 = "";
    }
}
2 голосов
/ 25 октября 2011

Вы должны использовать трубы, такие как:

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

Для получения дополнительной информации, проверьте: http://nodejs.org/docs/v0.5.10/api/streams.html#stream.pipe

РЕДАКТИРОВАТЬ: проблема в вашей реализации, кстати, состоит в том, что, делая это такими кусками, буфер записи не будет очищен, и вы будете читать весь файл перед тем, как писать большую его часть. отступить.

1 голос
/ 25 октября 2011

Согласно документации , data_output.write(...) вернет true, если строка была очищена, и false, если нет (из-за переполнения буфера ядра). Что это за поток?

Кроме того, я (довольно) уверен, что это не проблема, но: почему вы выделяете новый Buffer на каждой итерации цикла? Разве не имеет смысла инициализировать buf перед циклом?

0 голосов
/ 25 октября 2011

Я не знаю, как реализованы синхронные файловые функции, но рассматривали ли вы возможность использования асинхронных функций?Это с большей вероятностью позволило бы выполнить сборку мусора и сброс ввода-вывода.Таким образом, вместо цикла 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();
...