получение Java OutOfMemoryError: ошибка пространства кучи Java, которую я не могу отладить - PullRequest
3 голосов
/ 05 января 2012

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

Я упростил свой код, чтобы попытаться изолировать ошибку.Пожалуйста, взгляните на следующее:

File[] files = new File(args[0]).listFiles();

int filecnt = 0;

LinkedList<String> urls = new LinkedList<String>();

for (File f : files) {
    if (filecnt > 10) {
        System.exit(1);
    }

    System.out.println("Doing File " + filecnt + " of " + files.length + " :" +                f.getName());

    filecnt++;
    FileReader inputStream = null;
    StringBuilder builder = new StringBuilder();

    try {
        inputStream = new FileReader(f);
        int c;
        char d;

        while ((c = inputStream.read()) != -1) {
            d = (char)c;
            builder.append(d);
        }
    }

    finally {
        if (inputStream != null) {
            inputStream.close();
        }
    }   

    inputStream.close();

    String mystring = builder.toString();
    String temp[] = mystring.split("\\|NEWandrewLINE\\|");

    for (String s : temp) {
        String temp2[] = s.split("\\|NEWandrewTAB\\|");
        if (temp2.length == 22) { 
            urls.add(temp2[7].trim());
        }
    }
}

Я знаю, что этот код, вероятно, довольно запутанный :) У меня есть множество текстовых файлов в каталоге, который указан в args [0].Эти текстовые файлы были созданы мной.Я использовал | NEWandrewLINE |чтобы указать новую строку в текстовом файле, и | NEWandrewTAB |указать новый столбец.В этом фрагменте кода я пытаюсь получить доступ к URL каждой сохраненной строки (которая находится в 8-м столбце каждой строки).Итак, я прочитал весь текстовый файл.Строка разделена на | NEWandrewLINE |и затем снова разбить строку на подстроки в | NEWandrewTAB |.Я добавляю URL-адрес в LinkedList (называемый «urls») со строкой: urls.add (temp2 [7] .trim ())

Теперь результат выполнения этого кода:

Doing File 0 of 973 :results1322453406319.txt
Doing File 1 of 973 :results1322464193519.txt
Doing File 2 of 973 :results1322337493419.txt
Doing File 3 of 973 :results1322347332053.txt
Doing File 4 of 973 :results1322330379488.txt
Doing File 5 of 973 :results1322369464720.txt
Doing File 6 of 973 :results1322379574296.txt
Doing File 7 of 973 :results1322346981999.txt
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2882)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:572)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at Twitter.main(Twitter.java:86)

Где основная линия 86 относится к строке builder.append (d);в этом примере.

Но я не понимаю, что если я закомментирую строку urls.add (temp2 [7] .trim ());Я не получаю никакой ошибки.Таким образом, ошибка, по-видимому, вызвана переполнением связанного списка URL-адресов.Но почему тогда сообщаемая ошибка относится к StringBuilder?

Ответы [ 10 ]

4 голосов
/ 05 января 2012

Попробуйте заменить urls.add(temp2[7].trim()); на urls.add(new String(temp2[7].trim()));.

Я полагаю, что ваша проблема в том, что вы на самом деле сохраняете все содержимое файла, а не только извлеченное поле URL в своем списке URL, хотяне совсем очевидно.На самом деле это специфическая для реализации проблема с классом String, но обычно String # split и String # trim возвращают новые объекты String, которые содержат тот же внутренний массив char, что и исходная строка, и отличаются только полями смещения и длины.Использование конструктора new String(String) гарантирует, что вы сохраните только соответствующую часть исходных данных.

1 голос
/ 05 января 2012

Сколько у вас URL? Похоже, вы просто храните больше, чем можете.

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

Для ошибки OOM действительно не имеет значения, где она выброшена.

Чтобы проверить это правильно, используйте профилировщик (посмотрите на JVisualVM для бесплатного, и у вас, вероятно, уже есть). Вы увидите, какие объекты находятся в куче. Вы можете также сделать так, чтобы JVM сбрасывала свою память в файл при сбое, а затем анализировала этот файл с помощью visualvm. Вы должны увидеть, что одна вещь захватывает всю вашу память. Я подозреваю, что это все URL.

1 голос
/ 05 января 2012

Здесь уже есть несколько экспертов, поэтому я буду вкратце расскажу о проблемах:

  1. Неправильное использование String Builder:

StringBuilder builder = new StringBuilder ();

try {
    inputStream = new FileReader(f);
    int c;
    char d;

    while ((c = inputStream.read()) != -1) {
        d = (char)c;
        builder.append(d);
    }
}

Java прекрасна, когда вы обрабатываете небольшие объемы данных одновременно, помните сборщик мусора.

Вместо этого я бы порекомендовал вам читать файл (текстовый файл) по 1 строке за раз, обрабатывать строку и двигаться дальше, никогда не создавая огромный шарик памяти StringBuilder только для того, чтобы получить строку,

Размер вашего текстового файла составляет 1 ГБ, вы закончили, приятель.

  1. Добавить реальный процесс при чтении файла (как в элементе № 1)

  2. Вам не нужно снова закрывать InputStream, код в блоке finally достаточно хорош.

привет

1 голос
/ 05 января 2012

Простой ответ: вы не должны загружать все URL-адреса из текстовых файлов в память. Вы, безусловно, делаете это, потому что вы хотите обработать их на следующем шаге. Поэтому вместо добавления их в список в памяти выполните следующий шаг (возможно, сохраните в базе данных или проверьте, доступен ли он) и забудьте этот URL.

1 голос
/ 05 января 2012

Поскольку это

  1. из памяти, а не из кучи
  2. у вас есть много маленьких временных объектов

Я бы предложил вам датьваша JVM - максимальный размер кучи -X, который умещается в вашей оперативной памяти.

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

1 голос
/ 05 января 2012

Связанный список использует больше памяти каждый раз, когда вы добавляете строку. Это означает, что вам может не хватить памяти для сборки вашего StringBuilder.

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

0 голосов
/ 05 января 2012

Одной из основных проблем в вашем коде является то, что вы читаете весь файл в построитель строк, затем конвертируете его в строку и затем разбиваете на более мелкие части.Так что, если размер файла велик, у вас будут проблемы.Как предлагают другие, построчно обрабатывайте файл, так как это должно сэкономить много памяти.

Кроме того, вы должны проверить размер вашего списка после обработки каждого файла.Если размер очень большой, вы можете использовать другой подход или увеличить объем памяти для вашего процесса с помощью опции -Xmx.

0 голосов
/ 05 января 2012

Вместо того, чтобы пытаться разбить строку (которая в основном создает массив подстрок на основе разбиения) - таким образом, используя более чем удвоенную память каждый раз, когда вы используете slpit, вы должны попытаться выполнить сопоставление начала и регулярного выражения. Конечные шаблоны, извлекайте отдельные подстроки одну за другой, а затем извлекайте URL из этого.

Кроме того, если ваш файл большой, я бы посоветовал вам даже не загружать все это в память сразу ... передавать его содержимое в буфер (регулируемого размера) и использовать для этого поиск по шаблону (и продолжайте удалять / добавлять больше в буфер по мере продвижения по содержимому файла).

Реализация немного замедлит программу, но будет использовать значительно меньший объем памяти.

0 голосов
/ 05 января 2012

Вы читаете файлы в память. По крайней мере, один файл слишком велик, чтобы поместиться в кучу JVM по умолчанию. Вы можете разрешить ему использовать намного больше памяти с аргументом типа -Xmx1g в командной строке после java.

Кстати, действительно неэффективно читать файл по одному символу за раз!

0 голосов
/ 05 января 2012

, если связанный список потребляет вашу память, каждая команда, которая выделяет память впоследствии, может завершиться ошибкой OOM. Так что это похоже на вашу проблему.

...