Пространство кучи Java: Hashmap, ArrayList - PullRequest
0 голосов
/ 08 марта 2012

Я хотел бы обработать текстовый файл (около 400 МБ), чтобы создать рекурсивную структуру parent-child из данных, приведенных в каждой строке.Данные должны быть подготовлены для навигации сверху вниз (вход: родитель, выход: все дочерние и дочерние дочерние элементы).Например, строки для чтения: ( child , id1, id2, parent , id3)

132142086 ; 1; 2; 132528589 * * +1010; 132528599 132142087 ; 1; 3; * 1 013 * 132528589 ; 132528599 132142088 * * 1016; 1; 0; 132528589 ; 132528599 323442444 ; 1; 0; 132142088 * * тысяча двадцать-два; 132528599 454345434 ; 1; 0; * 1 025 * 323442444 * * тысяча двадцать-шесть; 132528599

132528589: является родителем 132142086,132142087,132142088132142088: является родителем 323442444323442444: является родителем 454345434

Дано: ОС Windows XP, 32 бита, 2 ГБ памяти и -Xmx1024m. Вот как я готовлю данные:

HashMap<String,ArrayList<String>> hMap=new HashMap<String,ArrayList<String>>();
  while ((myReader = bReader.readLine()) != null) 
          {
             String [] tmpObj=myReader.split(delimiter);
                   String valuesArrayS=tmpObj[0]+";"+tmpObj[1]+";"+tmpObj[2]+";"+tmpObj[3]+";"+tmpObj[4];
                        ArrayList<String> valuesArray=new ArrayList<String>();
                        //case of same key
                        if(hMap.containsKey(tmpObj[3]))
                            {
                            valuesArray=(ArrayList<String>)(hMap.get(tmpObj[3])).clone();
                            }

                        valuesArray.add(valuesArrayS);
                        hMap.put(tmpObj[3],valuesArray);
                        tmpObj=null;
                        valuesArray=null;
                        }

return hMap;

После этого я использую рекурсивную функцию:

HashMap<String,ArrayList<String>> getChildren(input parent)

для создания необходимой структуры данных.Планируется сделать hMap доступным (только для чтения) для более чем одного потока, используя функцию getChildren.Я протестировал эту программу с входным файлом размером 90 МБ, и, похоже, он работал правильно.Однако запуск его с реальным файлом с более чем 380 МБ приводит к:Исключение в потоке "main" java.lang.OutOfMemoryError: пространство кучи Java Мне нужна помощь в управлении ресурсами памяти

Ответы [ 4 ]

2 голосов
/ 08 марта 2012

Проверьте увеличение памяти, как советуют другие. Кроме того, вы можете лучше хранить свои данные в таблице, как рекомендует Sbodd и другие.

Однако вы можете столкнуться с фрагментацией памяти. Хеш-карты используют массивы. Большие хеш-карты используют большие массивы. Вы не указываете размер своей хэш-карты, поэтому каждый раз, когда он решает, что он должен быть больше, он отбрасывает свой старый массив и выделяет новый. Через некоторое время ваша память заполнится массивами хеш-таблиц, и вы получите исключение OutOfMemoryException, даже если у вас технически достаточно свободного места. (90% вашей памяти может быть доступно, но кусками слишком мало для использования.)

Сборщик мусора (GC) будет работать непрерывно, объединяя все эти свободные биты в блоки, достаточно большие для использования. Если бы ваша программа работала достаточно медленно, у вас не было бы проблемы, но ваша программа работает на полном наклоне, и сборщик мусора собирается отстать. GC сгенерирует исключение, если не сможет собрать достаточно большой блок достаточно быстро; тот факт, что память существует, не остановит ее. (Это означает, что программа, которую может запустить, не будет работать, но она не позволяет JVM работать очень медленно и выглядеть очень плохо для пользователей.)

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

Если вы не знаете, насколько большой может быть ваша таблица, используйте TreeMap. Это немного медленнее, но не выделяет огромные массивы и, следовательно, намного добрее для GC. Я считаю их лот более гибкими и полезными. Вы можете даже взглянуть на ConcurrentSkipTreeMap, который медленнее TreeMap, но позволяет добавлять, читать и удалять из нескольких потоков одновременно.

Но ваша лучшая ставка выглядит примерно так:

hMap = new HashMap<String,ArrayList<String>>( 10000000 );
2 голосов
/ 08 марта 2012

С точки зрения «простого подхода»: исходя из формулировки вашей проблемы, вам не нужно хранить id1, id2 или id3.Предполагая, что это так, как насчет замены вашего HashMap<String, ArrayList<String>> на HashMap<Integer, ArrayList<Integer>>?Вы можете использовать Integer.parseInt() для преобразования строки в int, и целое число всегда должно быть меньше соответствующей строки.

Другие предложения: замените ArrayList на HashSet, если вы нене заботьтесь о дубликатах.

За ответ outofBounds 'вам не нужно клонировать ArrayList каждый раз, когда вы хотите добавить к нему элемент.

0 голосов
/ 08 марта 2012

Вы действительно тестируете границы того, что можно делать с 1 ГБ памяти.

Вы могли бы:

  1. Увеличение пространства кучи. 32-битные окна будут ограничивать вас до ~ 1,5 ГБ, но у вас все еще есть немного больше места для маневра, этого может быть достаточно, чтобы перевернуть вас.
  2. Создайте некую утилиту препроцессора, которая предварительно разбивает файл на размеры, которые, как вы знаете, работают, и работает с ними по одному, возможно, иерархически.
  3. Попробуйте реструктурировать вашу программу. Это имеет много расщепления и объединения происходит. В Java строки неизменны и когда вы разбить строки и объединить с помощью + операторов, которые вы создаете новые строки все время (в 9 из 10 случаев это не имеет значения, но в вашем случае, когда вы работаете с очень ограниченным набором ресурсов, это может иметь значение)

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

На второй более полезной заметке (и забавно, если вы похожи на меня) - вы можете попробовать подключить jVisualVM к вашему приложению и попытаться понять, куда у вас куча, или использовать jhat и флаг -XX:+HeapDumpOnOutOfMemoryError jvm, чтобы увидеть, что происходило с кучей во время сбоя.

0 голосов
/ 08 марта 2012

Внутри цикла «Пока» вы можете уменьшить пространство, например,

String [] tmpObj=myReader.split(delimiter);
// String = String + String takes more Space than String.format(...)
//String valuesArrayS=tmpObj[0]+";"+tmpObj[1]+";"+tmpObj[2]+";"+tmpObj[3]+";"+tmpObj[4];

// Just Adding if thers is no List for a Key
if(!hMap.containsKey(tmpObj[3]){
    hMap.put(tmpObj[3], new ArrayList<String>());
}
// Gettin the list from the Map and adding the new stuff
List<String> values = hMap.get(tmpObj[3]);
values.add(String.format("%s;%s;%s;%s;%s",tmpObj[0], tmpObj[1], tmpObj[2], tmpObj[3], tmpObj[4]));

, не нужно клонировать список

...