Подтвердить что ты не робот

Как рассчитать использование памяти HashMap в Java?

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

Например:

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

Отображение выглядит следующим образом. Один ключ как строка представляет собой массив строк в качестве ключа.

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

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

4b9b3361

Ответ 1

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

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

Кувычки

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

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

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

например.

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

Вы также добавляете ссылки 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 ведер для ввода карты, тот факт, что размеры объектов, хранящихся в HashMap не влияют на размер HashMap, поскольку он хранит только ссылки).

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

Ответ 3

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

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