Почему размер логического примитива Java не определен?


111

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

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

Вышеизложенное подразумевает (хотя я, возможно, неправильно это понял), что тип данных int используется при работе с логическими значениями, но это 32-битная конструкция памяти. Учитывая, что логическое значение представляет только 1 бит информации:

  • Почему байтовый или короткий тип не используется в качестве прокси для логического значения вместо int?
  • Для любой данной JVM каков наиболее надежный способ узнать, сколько именно памяти используется для хранения логического типа?

Ответы:


116

Краткий ответ: да, логические значения обрабатываются как 32-битные сущности, но массивы логических значений используют 1 байт на элемент.

Более длинный ответ: JVM использует 32-битную ячейку стека, используемую для хранения локальных переменных, аргументов метода и значений выражений. Примитивы, размер которых меньше 1 ячейки, заполняются, примитивы размером более 32 бит (длинные и двойные) занимают 2 ячейки. Этот метод минимизирует количество кодов операций, но имеет некоторые специфические побочные эффекты (например, необходимость маскировать байты).

Примитивы, хранящиеся в массивах, могут использовать менее 32 битов, и существуют разные коды операций для загрузки и хранения примитивных значений из массива. И для логических, и для байтовых значений используются символы baloadиbastore опкоды, что подразумевает , что булевы массивы берут 1 байт для каждого элемента.

Что касается макета объекта в памяти, это подпадает под правила «частной реализации» , это может быть 1 бит, 1 байт или, как отмечалось на другом плакате, выравнивание по границе 64-битного двойного слова. Скорее всего, он принимает базовый размер слова базового оборудования (32 или 64 бита).


Что касается минимизации объема пространства, используемого логическими значениями: это действительно не проблема для большинства приложений. Фреймы стека (содержащие локальные переменные и аргументы метода) не очень большие, и в большой схеме дискретное логическое значение в объекте тоже не такое большое. Если у вас много объектов с большим количеством логических значений, вы можете использовать битовые поля, которые управляются через ваши геттеры и сеттеры. Однако вы заплатите штраф за процессорное время, который, вероятно, больше, чем штраф за память.


Верно ли, что для булевых / байтовых членов класса они тоже 4 байта? Экземпляр класса выделяется в стеке целиком, поэтому я могу представить, что JVM, вероятно, должна использовать 1 байт на логический / байтовый член и, наконец, сделать 4-байтовое выравнивание для всего экземпляра класса. Это так? (если у вас есть ссылки, подтверждающие это, поделитесь, пожалуйста)
dma_k 08

@dma_k: как отмечалось в моем ответе, макет экземпляра класса зависит от реализации. Однако обратите внимание, что экземпляры классов не хранятся в стеке, они хранятся в куче (хотя вы увидите некоторые ссылки на JDK 7 "анализ выхода", перемещающий объекты из стека в кучу, это, похоже, не так; см. java.sun.com/javase/7/docs/technotes/guides/vm/…)
kdgregory

1
Иногда упаковка логических значений может быть быстрее. Если размер кеша имеет значение, лучше упаковать вещи. Например, сегментированное первичное сито работает с фрагментами по 32 КБ (размер кэша L1) намного быстрее, чем несегментированное сито. Между кусками есть некоторые накладные расходы, и с упаковкой вы платите накладные расходы в восемь раз меньше. Я еще не измерял.
maaartinus 01

7

Одно логическое значение где-то в иерархии наследования может использовать до 8 байтов! Это связано с заполнением. Более подробную информацию можно найти в разделе Сколько памяти используется моим объектом Java? :

Возвращаясь к вопросу о том, сколько потребляет логическое значение, да, оно потребляет хотя бы один байт, но из-за правил выравнивания оно может потреблять гораздо больше. IMHO более интересно знать, что boolean [] будет потреблять один байт на запись, а не один бит, плюс некоторые накладные расходы из-за выравнивания и для поля размера массива. Существуют алгоритмы графов, в которых полезны большие поля битов, и вам нужно знать, что если вы используете boolean [], вам нужно почти ровно в 8 раз больше памяти, чем действительно необходимо (1 байт против 1 бита).


Как бы вообще использовать логическое значение []?
Томас Юнг,

boolean [] может использоваться для маски. Иногда BitSet может быть лучше, потому что у него есть несколько полезных методов.
Майкл Манси

5

В 5-м издании Java in a Nutshell (O'Reilly) говорится, что логический примитивный тип равен 1 байту. Это могло быть неправильно, если судить по результатам исследования кучи. Интересно, есть ли у большинства JVM проблемы с выделением менее одного байта для переменных.


3

Логическое отображение было выполнено с учетом 32-битного процессора. Значение int имеет 32 бита, поэтому его можно обработать за одну операцию.

Вот решение Питера Норвига по Java IAQ: Часто ответы на вопросы для измерения размера (с некоторой неточностью):

static Runtime runtime = Runtime.getRuntime();
...
long start, end;
Object obj;
runtime.gc();
start = runtime.freememory();
obj = new Object(); // Or whatever you want to look at
end =  runtime.freememory();
System.out.println("That took " + (start-end) + " bytes.");

Поскольку этот разговор касается примитивов, вам нужно проявить изобретательность в тестировании, поскольку примитивы не хранятся в куче, если они не являются полем в экземпляре или массиве. И ни один из них не отвечает на вопрос о том, как Java в любом случае решит хранить его в стеке.
Джесси

2

ЦП работают с определенным типом данных. В случае 32-битных процессоров они имеют длину 32 бита и, следовательно, то, что вы называете int в Java. Все, что ниже или выше, должно быть заполнено или разделено на эту длину, прежде чем ЦП сможет обработать это. Это не займет много времени, но если вам нужно 2 цикла ЦП вместо 1 для основных операций, это означает удвоение затрат / времени.

Эта спецификация предназначена для 32-битных процессоров, чтобы они могли обрабатывать логические значения с их родным типом данных.

Здесь может быть только одно: скорость или память - SUN выбрал скорость.


1

Логическое значение представляет собой один бит информации, но его «размер» не является чем-то точно определенным, как говорится в руководствах Sun Java. У логических литералов есть только два возможных значения: истина и ложь. Подробнее см. Типы данных Java .


-10

Почему бы не сделать такой файл .java:

Empty.java

class Empty{
}

и один такой класс:

NotEmpty.java

class NotEmpty{
   boolean b;
}

Скомпилируйте их оба и сравните файлы .class с шестнадцатеричным редактором.


5
это вообще еще одна метрика, не связанная с размером примитивного логического типа в памяти.
Джоэл
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.