Как рассчитать использование памяти HashMap в Java? - PullRequest
23 голосов
/ 28 мая 2011

В одном из интервью мне было предложено рассчитать использование памяти для HashMap и какой объем памяти он будет использовать, если в нем будет 2 миллиона элементов.

Например:

Map <String,List<String>> mp=new HashMap <String,List<String>>();

Отображение похоже на это.

key   value
----- ---------------------------
abc   ['hello','how']
abz   ['hello','how','are','you']

Как бы я оценил использование памяти этим объектом HashMap в Java?

Ответы [ 3 ]

22 голосов
/ 28 мая 2011

Краткий ответ

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

Придирки

Если части объекта повторно используются в других структурах, например Строковые литералы, вы не освободите столько памяти, отбросив ее. Фактически, отбрасывание одной ссылки на HashMap может вообще не освободить память.

А как насчет сериализации?

Сериализация объекта - один из подходов к получению оценки, но он может быть диким, так как издержки сериализации и кодировка различаются в памяти и в байтовом потоке. Сколько памяти используется, зависит от JVM (и от того, использует ли она 32/64-битные ссылки), но формат сериализации всегда одинаков.

, например

В JVM от Sun / Oracle Integer может принимать 16 байтов для заголовка, 4 байта для числа и заполнение 4 байта (объекты выровнены в памяти по 8 байтов), всего 24 байта. Однако, если вы сериализуете одно целое число, оно занимает 81 байт, сериализует два целых числа и 91 байт. то есть размер первого целого числа надувается, а второе целое меньше того, что используется в памяти.

Строка - гораздо более сложный пример. В JVM Sun / Oracle он содержит значения 3 int и ссылку char[]. Таким образом, вы можете предположить, что он использует 16-байтовый заголовок плюс 3 * 4 байта для int с, 4 байта для char[], 16 байтов для служебной информации char[] и затем два байта на символ, выровненные по 8- граница байта ...

Какие флаги могут изменить размер?

Если у вас есть 64-битные ссылки, ссылка char[] имеет длину 8 байтов, что приводит к заполнению 4 байтами. Если у вас есть 64-битная JVM, вы можете использовать +XX:+UseCompressedOops для использования 32-битных ссылок. (Посмотрите, что размер бит JVM сам по себе не говорит о размере ссылок)

Если у вас есть -XX:+UseCompressedStrings, JVM будет использовать байт [] вместо массива символов, когда это возможно. Это может немного замедлить ваше приложение, но может значительно увеличить потребление памяти. Когда используется байт [], потребляемая память составляет 1 байт на символ. ;) Примечание: для строки из 4 символов, как в примере, используемый размер такой же из-за 8-байтовой границы.

Что вы подразумеваете под "размером"?

Как уже указывалось, HashMap и List более сложны, поскольку многие, если не все, строки могут использоваться повторно, возможно, строковые литералы. То, что вы подразумеваете под «размером», зависит от того, как он используется. т.е. сколько памяти будет использовать структура в одиночку? Сколько было бы освобождено, если бы структура была отброшена? Сколько памяти будет использовано, если вы скопируете структуру? На эти вопросы могут быть разные ответы.

Что вы можете сделать без профилировщика?

Если вы можете определить, что вероятный консервативный размер достаточно мал, точный размер не имеет значения. В консервативном случае, скорее всего, вы создадите каждую строку и запись с нуля. (Скорее всего, HashMap может вместить 1 миллиард записей, даже если он пуст. Строки с одним символом могут быть подстрокой строки с 2 миллиардами символов)

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

(Кстати, хотя System.gc () является лишь подсказкой, JVM Sun / Oracle будет выполнять полный GC каждый раз по умолчанию)

1 голос
/ 24 апреля 2013

Я думаю, что вопрос следует прояснить, потому что есть разница между размером HashMap и размером HashMap + объектов, содержащихся в HashMap.

Если вы учитываете размер HashMap, в приведенном вами примере HashMap хранит одну ссылку на строку «aby» и одну ссылку на список. Таким образом, несколько элементов в списке не имеют значения. В значении сохраняется только ссылка на список.

В 32-битной JVM, в одной записи Map, у вас есть 4 байта для ссылки «aby» + 4 байта для ссылки на List + 4 байта для свойства int «hashcode» записи Map + 4 байта для « следующее "свойство записи карты.

Вы также добавляете ссылки в 4 * (X-1) байта, где «X» - это количество пустых сегментов, созданных HashMap при вызове конструктора new HashMap<String,List<String>>() , Согласно http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html, должно быть 16.

Существуют также loadFactor, modCount, порог и размер, которые представляют собой примитивный тип int (еще 16 байтов) и заголовок (8 байтов).

Таким образом, в итоге размер вашей вышеупомянутой HashMap будет 4 + 4 + 1 + (4 * 15) + 16 + 8 = 93 байта

Это приблизительное значение, основанное на данных, которыми владеет HashMap. Я думаю, что, возможно, интервьюеру было интересно узнать, знаете ли вы о том, как работает HashMap (например, тот факт, что конструктор по умолчанию создает массив из 16 блоков для записи Map, тот факт, что размеры объектов хранятся в HashMap не влияют на размер HashMap, поскольку он хранит только ссылки).

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

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

вы не можете знать заранее, не зная, что все строки и сколько элементов в каждом списке, или не зная, являются ли все строки уникальными ссылками.

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

...