Какова стоимость хранения примитива в штучной упаковке в Java?

Насколько велик в байтах примитив в штучной упаковке, такой как java.lang.Integer или java.lang.Character в Java?

int имеет размер 4 байта, типичный указатель также имеет размер 4 байта (если он не сжат JVM). Является ли стоимость целого числа (без кэширования) таким образом 4 bytes + 4 bytes = 8 bytes? Существуют ли какие-либо еще скрытые поля в объекте-коробке или дополнительные накладные расходы, связанные с объектами (т.е. есть ли общие затраты на объекты, о которых я не знаю?).

Меня не интересуют вопросы кэширования. Я знаю, что целые числа в пределах определенного диапазона кэшируются JVM.

Можно перефразировать вопрос: на какой максимальный коэффициент умножается объем памяти, используемый для упакованных значений, по сравнению с примитивными значениями?

EDIT: я понимаю, что существует несколько реализаций JVM. Какова типичная стоимость типичной реализации 32-разрядной точки доступа?


person scravy    schedule 27.01.2012    source источник
comment
С определенной точки зрения на этот вопрос нет ответа, поскольку накладные расходы на упакованный примитив не указаны ни в одной спецификации. Он может и будет варьироваться от ВМ к ВМ и от платформы к платформе. Аппаратная платформа с тегированной памятью может даже не иметь накладных расходов.   -  person Geoff Reedy    schedule 27.01.2012
comment
дубликат в Java , как лучше всего определить размер объекта?.   -  person DwB    schedule 27.01.2012
comment
Я думаю, это также может зависеть от контекста, в котором вы используете упакованный int, и от любых оптимизаций, которые делает компилятор времени выполнения.   -  person Garrett Hall    schedule 27.01.2012


Ответы (4)


Это определяется реализацией, поэтому конкретного ответа нет. Но я должен быть в состоянии ответить на него для Hotspot.

Что вам нужно знать: Hotspot всегда выравнивает объекты по границам 8 байт. Кроме того, для каждого объекта есть 2 слова накладных расходов. [1]

Если мы сложим это вместе, то получим:

32-битная виртуальная машина: 4 байта целого + 2 слова заголовка объекта = 12 байт. Это не кратно 8, поэтому в результате стоимость 1 целого числа равна следующему кратному 8: 16 байт.

64-битная виртуальная машина: 4 байта целого числа + 2 слова = 20 байт. Снова округляем: размер 24 байта.

Размер ссылки, очевидно, не влияет на размер самого объекта, за исключением случаев, когда он имеет ссылки на другие объекты, что не относится к простой оболочке int. Если бы это было так, у нас было бы 4 байта на ссылку для 32-битных и 4 байта для кучи ‹= 32 ГБ с CompressedOops на современных JVM (иначе 8 байт) для 64-битных JVM.

[1] Заинтересованные могут посмотреть код в share/vm/oops/oop.hpp

person Voo    schedule 27.01.2012
comment
для полноты: если объект синхронизирован, будет собственная структура, содержащая мьютекс. - person bestsss; 30.01.2012
comment
@bestsss Правильно, полностью проигнорировал это. Я думаю, что тяжелые блокировки создаются только в том случае, если у нас есть оспариваемая блокировка (при условии предвзятой блокировки). Если мы этого не делаем и не используем хэш-код объекта, я думаю, что Hotspot сохраняет tid в самом заголовке объекта. Так что просто вызов synchronized(foo) может не обязательно выделять какую-либо память. - person Voo; 30.01.2012
comment
да. неоспариваемые предвзятые блокировки не увеличивают заголовок, если есть запрос System.identityHashCode(). Последний используется (неофициально) для предотвращения предвзятой блокировки выбранного объекта. (я имею в виду в основном довольный -> иногда я использовал Object lock =new Byte(1) для упрощения сериализации - person bestsss; 30.01.2012

Это больше, чем это.

Каждая ссылка на объект имеет дополнительные накладные расходы, например ссылка на класс. Мало того, ваш 4-байтовый указатель не совсем точен. Это ссылка, поэтому это идентификатор плюс указатель, И этот указатель может быть 8 байтов, если вы используете 64-битную JVM.

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

Моя (Super SWAG) оценка была бы такой. Ссылка на объект 16 байтов (64-битная JVM) Ссылка на класс 16 байтов примитивное значение 4 байта (при условии, что это целое число) Всего. 36 байт.

РЕДАКТИРОВАТЬ: Теперь, когда вы укажете 32-битную JVM, мой SWAG будет 20 байтов, используя ту же математику, что и выше.

person rfeak    schedule 27.01.2012
comment
У меня нет ни малейшего представления о том, как вы пришли к этим числам ... глядя на мой исходный код Hotspot в oop.hpp, я тоже ничего не понимаю (я могу найти там только обычные 2 указателя) - person Voo; 27.01.2012

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

Вот выдержка из книги Джошуа Блоха «Эффективная Java» (2-е издание), которая должна помочь вам принять решение:

"So when should you use boxed primitives? They have several legitimate uses. The first is as elements, keys, and values in collections. You can’t put primitives in collections, so you’re forced to use boxed primitives. This is a special case of a more general one. You must use boxed primitives as type parameters in parame- terized types (Chapter 5), because the language does not permit you to use primi- tives. For example, you cannot declare a variable to be of type Thread- Local<int>, so you must use ThreadLocal<Integer> instead. Finally, you must use boxed primitives when making reflective method invocations (Item 53).

In summary, use primitives in preference to boxed primitives whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw a NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations."

Надеюсь, это поможет.

person Alex    schedule 27.01.2012

Одно очень небольшое дополнение к этим ответам заключается в том, что для примитивов в штучной упаковке происходит некоторая дедупликация. Например, Integer::valueOf(int) использует java.lang.IntegerCache, который использует экземпляры Integer со значениями в диапазоне -128..127. Итак, у вас есть вышеупомянутые размеры для коробочных объектов, но не каждый из них будет отдельным объектом.

person Imaskar    schedule 13.09.2019