В Java выделение пустого пространства HashMap - PullRequest
4 голосов
/ 01 марта 2011

Как узнать, сколько места занимает предварительно заданный размер HashMap, прежде чем будут добавлены какие-либо элементы? Например, как определить, сколько памяти занимает следующее?

HashMap<String, Object> map = new HashMap<String, Object>(1000000);

Ответы [ 8 ]

4 голосов
/ 01 марта 2011

В принципе, вы можете:

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

Большинство других ответов касаютсяВторой способ, поэтому я посмотрю на первый (в исходном коде OpenJDK, 1.6.0_20).

Конструктор использует capacity, который является следующей степенью двух> = вашего параметра initialCapacity, таким образом, 1048576= 2 ^ 20 в нашем случае.Затем он создает new Entry[capacity] и присваивает его переменной table.(Кроме того, он присваивает некоторые примитивные переменные).

Итак, теперь у нас есть один довольно маленький объект HashMap (он содержит только 3 целых, одну переменную и одну ссылочную переменную) и один довольно большой объект Entry [].Этому массиву требуется место для их элементов массива (которые являются нормальными ссылочными переменными) и некоторых метаданных (размер, класс).

Итак, все сводится к тому, насколько велика ссылочная переменная.Это зависит от реализации виртуальных машин - обычно в 32-битных виртуальных машинах это 32-битные (= 4 байта), в 64-битных виртуальных машинах 64-битные (= 8 байтов).

Итак, в основном на 32-битных виртуальныхмассив занимает 4 МБ, на 64-битных виртуальных машинах - 8 МБ, а также незначительные административные данные.


Если затем заполнить HashTable сопоставлениями, каждое сопоставление соответствует объекту Entry.Этот объект ввода состоит из одного целого и трех ссылок, занимающих около 24 байтов на 32-битных виртуальных машинах, возможно, двойного на 64-битных виртуальных машинах.Таким образом, ваш HashMap с отображением 1000000 (при условии, что коэффициент загрузки> 1) будет занимать ~ 28 МБ на 32-битных виртуальных машинах и ~ 56 МБ на 64-битных виртуальных машинах.

В дополнение к самим объектам ключа и значения,конечно.

2 голосов
/ 01 марта 2011

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

Но, для простоты, размер, вероятно, будет либо ~4 * 2^20, либо ~8 * 2^20 байтов, для 32-битной или 64-битной jvm соответственно.

Обоснование:

  • Реализация HashMap в Sun Java 1.6 имеет фиксированный боковой объект верхнего уровня и поле table, которое указывает на массив ссылок на хэш-цепочки.

  • Во вновь созданном (пустом) HashMap все ссылки равны null, а размер массива равен следующей степени на два больше, чем предоставленный initialCapacity. (Да ... Я проверил исходный код.)

  • Ссылка занимает 4 байта в типичной 32-битной JVM и 8 байтов в типичной 64-битной JVM. Некоторые 64-битные JVM поддерживают компактные ссылки («сжатые операции»), но для этого необходимо установить параметры JVM.

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

  • У верхнего объекта и массива есть заголовки объекта, но они постоянны и относительно малы.

Таким образом, размер массива table преобладает, и он 2^20 (следующая степень 2 больше 1,000,000), умноженный на размер ссылки.


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

2 голосов
/ 01 марта 2011

Вы можете проверить использование памяти до и после создания переменной.Например:

long preMemUsage = Runtime.getRuntime().totalMemory() -
      Runtime.getRuntime().freeMemory();
HashMap<String> map = new HashMap<String>(1000000);
long postMemUsage = Runtime.getRuntime().totalMemory() -
      Runtime.getRuntime().freeMemory();
1 голос
/ 01 марта 2011

Я бы взглянул на эту статью: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html

Короче говоря, в Java нет оператора sizeof в стиле C.Вы можете использовать инструменты профилирования, но IMO по приведенной выше ссылке дает самое простое решение.

Еще одна полезная информация: пустая строка Java занимает 40 байт.Один миллион из них, вероятно, будет не менее 40 МБ ...

1 голос
/ 01 марта 2011

Возможно, вы могли бы использовать профилировщик, например VisualVM , и отслеживать использование памяти.

Также посмотрите на это: http://www.velocityreviews.com/forums/t148009-java-hashmap-size.html

0 голосов
/ 05 июня 2014

В последней версии Java 1.7 (я смотрю на 1.7.0_55) HashMap фактически лениво создает свою внутреннюю таблицу .Он создается только при вызове метода put () - см. Закрытый метод "inflateTable ()".Таким образом, ваш HashMap, прежде чем вы добавите к нему хотя бы что-нибудь, будет занимать только несколько байтов заголовка объекта и полей экземпляра.

0 голосов
/ 01 марта 2011

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

0 голосов
/ 01 марта 2011

Вы должны иметь возможность использовать VisualVM (поставляется с JDK 6 или может быть загружен ) для создания снимка памяти и проверки выделенных объектов на предмет их размера.

НТН

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