Ошибка нехватки памяти при загрузке текстового файла в список, хотя я указал достаточно большой xmx - PullRequest
1 голос
/ 07 февраля 2012

При попытке загрузить текст объемом 39 МБ в список возникает ошибка -Xms32m -Xmx128m. поэтому я начал постепенно увеличивать Xmx до тех пор, пока он не загрузился успешно, и обнаружил, что мне нужно по крайней мере Xmx170m для загрузки файла 39 МБ в память

Мне интересно, зачем мне такой большой объем памяти? Я пытаюсь вычислить объем памяти, выделяемой в списке, используя UTF-8, UTF-16 и UTF-32, но ни один из них, похоже, не соответствует Xmx в точке, где происходит исключение из-за недостатка памяти. Итак, как правильно рассчитать выделенную память?

Может кто-нибудь объяснить, что мне здесь не хватает?

Ниже приведен пример вывода и кода с -Xms32m -Xmx128m

Max memory 129 MB.
Total memory 32 MB.
Free memory 32 MB.
Input file size 39 MB.
Out Of Memory Error
List size in UFT-8 29 MB.
List size in UFT-16 58 MB.
List size in UFT-32 116 MB.
Free memory 4 MB.
End 

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.ArrayList.ensureCapacity(Unknown Source)
    at java.util.ArrayList.add(Unknown Source)
    at com.nrx.util.SortUtil.main(SortUtil.java:288)



public static void main(String[] args)
{
    System.out.println("Max memory "+Runtime.getRuntime().maxMemory()/1000 /1000+" MB.");
    System.out.println("Total memory "+Runtime.getRuntime().totalMemory()/1000 /1000+" MB.");
    System.out.println("Free memory "+Runtime.getRuntime().freeMemory()/1000 /1000+" MB.");

    long utf8 = 0;
    long utf16 = 0;
    long utf32 = 0;
    List<String> strList = new ArrayList<String>();
    try 
    {
        File inFile = new File("data/input38.log");
        System.out.println("Input file size "+inFile.length()/1000 /1000+" MB.");
        BufferedReader fileReader = new BufferedReader(new FileReader(inFile));
        String line = fileReader.readLine();
        while (line != null)
        {
            utf8 = utf8 + line.getBytes("UTF-8").length;
            utf16 = utf16 + line.getBytes("UTF-16").length;
            utf32 = utf32 + line.getBytes("UTF-32").length;

            StringTokenizer st = new StringTokenizer(line, " ");
            while(st.hasMoreTokens())
                strList.add(st.nextToken().trim());
            line = fileReader.readLine();
        }

    } 
    catch (OutOfMemoryError e) 
    {
        System.out.println("Out Of Memory Error ");
        System.out.println("List size in UFT-8 "+utf8/1000 /1000+" MB.");
        System.out.println("List size in UFT-16 "+utf16/1000 /1000+" MB.");
        System.out.println("List size in UFT-32 "+utf32/1000 /1000+" MB.");
        System.out.println("Free memory "+Runtime.getRuntime().freeMemory()/1000 /1000+" MB.");
        e.printStackTrace();
    }
    catch (FileNotFoundException e) 
    {
        e.printStackTrace();
    } 
    catch (IOException e) 
    {
        e.printStackTrace();
    }
    System.out.println("End ");
}

Ответы [ 4 ]

2 голосов
/ 07 февраля 2012

Я считаю, что это потому, что вы используете ArrayList. ArrayList в умной обертке над простым массивом. Когда список растет, ArrayList создает новый массив и копирует старый контент в новый. Во-первых, это крайне не эффективно. Во-вторых, каждый раз требуется тройной размер списка: n элементов в старом массиве и n * 2 элемента в новом.

Итак, вместо этого попробуйте использовать LinkedList. Я надеюсь, что это сработает для вас.

1 голос
/ 07 февраля 2012

Вы используете ArrayList.Так что это список на основе массива.Невозможно изменить размер массива без создания нового, большего.Новый массив должен быть размещен, и все элементы должны быть скопированы в больший (с некоторым пустым пространством, чтобы сделать добавление некоторого количества элементов не таким тяжелым).Попробуйте использовать таблицу String [] с указанным количеством элементов, чтобы минимизировать ее размер в памяти и избежать копирования массива.

И я не уверен, но я думаю, что в Java символы в строках всегда 16-bit?

И строки в Java совместно используются и оптимизируются, поэтому вычисление размера строки не является тривиальной операцией.

Редактировать: я вижу, что кто-то упоминает о LinkedList, помните, что в этом спискевсегда есть дополнительные переменные-указатели, которые также должны храниться в памяти.

0 голосов
/ 07 февраля 2012

Емкость ArrayList по умолчанию равна 10, после этого ее емкость удваивается, поэтому, если у вас есть 100 000 элементов, реальная выделенная емкость может быть 200 000, что может быть причиной этого исключения

0 голосов
/ 07 февраля 2012

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

Для лучшего изображения вы можете попытаться определить, сколько дополнительной памяти требуетсяскажем, каждые 10 МБ увеличение размера файла.Некоторый объем памяти будет постоянным и необходим независимо от размера файла.

Во-вторых, вы также должны измерить память после выполнения полного GC.Вы можете увидеть, где используется память с помощью JVisualVm.

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