Чтение большого файла в Java - пространство кучи Java - PullRequest
7 голосов
/ 05 мая 2011

Я читаю большой файл tsv (~ 40G) и пытаюсь сократить его, читая построчно и печатая только определенные строки в новый файл. Однако я продолжаю получать следующее исключение:

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2894)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:117)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:532)
    at java.lang.StringBuffer.append(StringBuffer.java:323)
    at java.io.BufferedReader.readLine(BufferedReader.java:362)
    at java.io.BufferedReader.readLine(BufferedReader.java:379)

Ниже приведена основная часть кода. Я на всякий случай указал размер буфера 8192. Разве Java не очищает буфер после достижения предела размера буфера? Я не вижу, что может вызвать здесь большое использование памяти. Я пытался увеличить размер кучи, но это не имело никакого значения (машина с 4 ГБ ОЗУ). Я также пытался очищать выходной файл каждые X строк, но это тоже не помогло. Я думаю, может быть, мне нужно позвонить в GC, но это звучит неправильно.

Есть мысли? Большое спасибо. Кстати, я знаю, что должен вызывать trim () только один раз, сохранить его и затем использовать.

Set<String> set = new HashSet<String>();
set.add("A-B");
...
...
static public void main(String[] args) throws Exception
{
   BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile),"UTF-8"), 8192);
   PrintStream output = new PrintStream(outputFile, "UTF-8");

   String line = reader.readLine();
   while(line!=null){
        String[] fields = line.split("\t");
        if( set.contains(fields[0].trim()+"-"+fields[1].trim()) )
            output.println((fields[0].trim()+"-"+fields[1].trim()));

        line = reader.readLine();
   }

output.close();

}

Ответы [ 5 ]

17 голосов
/ 05 мая 2011

Скорее всего, происходит то, что в файле нет разделителей строк, и поэтому читатель продолжает наращивать свой неограниченный StringBuffer до тех пор, пока не закончится память.

Решение состоит в том, чтобы считывать фиксированное количество байтов за раз, используя метод чтения «read», а затем искать новые строки (или другие токены синтаксического анализа) в пределах меньшего буфера (ов).

3 голосов
/ 05 мая 2011

Вы уверены, что "строки" в файле разделены новыми строками?

2 голосов
/ 05 мая 2011

У меня есть 3 теории:

  • Входным файлом является не UTF-8, а какой-то неопределенный двоичный формат, который приводит к очень длинным строкам при чтении в формате UTF-8.

  • Файл содержит несколько очень длинных "строк" ... или вообще нет разрывов строк.

  • Что-то еще происходит в коде, который вы не показываетенас;например, вы добавляете новые элементы в set.


Чтобы помочь диагностировать это:

  • Используйте какой-нибудь инструмент, например od (в UNIX/ LINUX) для подтверждения того, что входной файл действительно содержит допустимые ограничители строки;т. е. CR, NL или CR NL.
  • Используйте какой-либо инструмент, чтобы проверить, является ли файл допустимым UTF-8.
  • Добавьте статический счетчик строк в свой код, а также, когда приложение взорветсяс OOME, распечатайте значение счетчика строки.
  • Следите за самой длинной линией, которую вы когда-либо видели, и распечатайте ее, когда получите OOME.

Для записи, ваше слегка неоптимальное использование trim не будет иметь никакого отношения к этому вопросу.

1 голос
/ 05 мая 2011

Одна из возможностей заключается в том, что вам не хватает места в куче во время сборки мусора.JVM Hotspot по умолчанию использует параллельный сборщик, что означает, что ваше приложение может распределять объекты быстрее, чем сборщик может их вернуть.Я был в состоянии вызвать OutOfMemoryError с предположительно только 10K живых (маленьких) объектов, быстро выделяя и отбрасывая.

Вы можете попробовать вместо этого использовать старый (до 1.5) последовательный сборщик с опцией -XX:+UseSerialGC,Есть несколько других «расширенных» опций , которые вы можете использовать для настройки коллекции.

0 голосов
/ 05 мая 2011

Вы можете попробовать удалить объявление String[] fields из цикла.Как вы создаете новый массив в каждом цикле.Вы можете просто использовать старый, верно?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...