boolean size not defined in java: why?
boolean represents one bit of information, but its «size» isn’t something that’s precisely defined.
Question came to my mind was why boolean in java can’t be represented with 1 bit(or 1 byte if byte is minimum representation ) ?
the JVM uses a 32-bit stack cell, used to hold local variables, method arguments, and expression values. Primitives that are smaller than 1 cell are padded out, primitives larger than 32 bits (long and double) take 2 cells
Does it mean even byte/char/short primitiva data types also take 32 bit though their size is defined as 8/16/16 bit ?
Also can we say boolean size will be 32 bit on 32 bit cpu and 64 bit on 64 bit cpu ?
2 Answers 2
TL;DR The only thing that’s sure is that boolean occupies at least one bit. Everything else depends on the JVM implementation.
The Java Language Specification doesn’t define sizes, only value ranges (see The Language Spec). So, it’s not only the boolean size that’s undefined at this level. And boolean has two possible values: false and true .
The Virtual Machine Specification tells us that boolean variables are treated like int with values 0 and 1. Only arrays of boolean have specific support. So at the Virtual Machine level, a boolean variable occupies the same amount of space as an int , meaning one stack cell: at least 4 bytes, typically 4 bytes on 32-bit Java and 8 bytes on 64-bit.
Finally there’s the HotSpot engine that compiles JVM bytecode into optimized CPU-specific machine code, and I bet that in many cases it’s able to deduce the limited value-range of an int -masked boolean from the context and use a smaller size.
There are a number of concepts to tease apart:
- the Java programming language itself, which is a textual programming language,
- the Java Virtual Machine byte-code & class file format, which is a binary compiled encoding of original Java language source code, and is used as an interchange file format to store, load, and share java object code,
- a particular Java Virtual Machine implementation, which could be an interpreter though is often instead a JIT-based implementation,
- JIT generated machine code that runs directly on the hardware processor.
Java, the programming language, doesn’t define a concept size of primitive types because (unlike C/C++) there is no sizeof operator : sizes are not observable via language constructs, so the language doesn’t need to define them.
As @Ralf points out the Java language does define the range of the primitive types, which is very relevant to the programmer as these ranges can be observed via constructs within the language.
The language does define an instrumentation capability that allows inquiry into the size of an object, but (1) this requires instrumentation, (2) provides only an estimate, and (3) this inquiry does not apply to primitive types or local variables.
the JVM uses a 32-bit stack cell, used to hold local variables, method arguments, and expression values. Primitives that are smaller than 1 cell are padded out, primitives larger than 32 bits (long and double) take 2 cells
The padding quote speaks to details of the JVM class file format, which is being used as an interchange mechanism (as distinct from the Java language and a JVM implementation). Though what it says holds for the abstract machine and JVM byte code, it does not necessarily have to hold for the JIT’ed machine code.
The padding quote also restricts itself to discussion of local variables/parameters/expressions that are typically stack allocated (e.g. auto or automatics in C/C++), and does not discuss object/arrays.
The actual size of such automatic variables is almost never an issue (e.g. for performance or for space).
In part, this is because the underlying hardware CPUs more naturally works on larger bit sizes (like 32 or 64) rather than 1-bit. Even 8 or 16 bit sizes are generally no faster than 32, and sometimes 8-bit handling requires an extra instruction or two to work with the hardware instruction set’s wider registers.
And another reason is limited usage of local variables — they are used directly by code and only by code, and thus not really subject to scaling issues — in particular, as compared to objects and arrays, which are used by data structures of potentially any scale.
(We might consider recursion as scaling of local variables, so a larger local variables in recursive routines risks stack overflow sooner.)
However, sizes of objects can matter a lot, if the count of instances is high, and also, sizes of array elements can matter if having high number of elements.
Does it mean even byte/char/short primitiva data types also take 32 bit though their size is defined as 8/16/16 bit ?
For locals, maybe, maybe not depending on the JIT.
For objects, within the JVM byte code & class file mechanism, the fields are directly access by their identification and there is no notion given of «cells» — whereas there is with the (local and parameter) variables.
Список базовых типов данных
— Ты уже изучил основы синтаксиса Java, поэтому я хочу рассказать тебе некоторые вещи более углубленно.
— Сегодня я расскажу тебе немного про примитивные типы, а также — сколько памяти они занимают. Тебе это пригодится и, возможно, даже сегодня. Вот эти типы:
Тип | Размер, байт |
Диапазон значений | Значение по умолчанию | Описание |
---|---|---|---|---|
byte | 1 | -128 .. 127 | 0 | Самое маленькое целое — один байт |
short | 2 | -32,768 .. 32,767 | 0 | Короткое целое, два байта |
int | 4 | — 2*10 9 .. 2*10 9 | 0 | Целое число, 4 байта |
long | 8 | — 9*10 18 .. 9*10 18 | 0L | Длинное целое, 8 байт |
float | 4 | -10 127 .. 10 127 | 0.0f | Дробное, 4 байта |
double | 8 | -10 1023 .. 10 1023 | 0.0d | Дробное, двойной длины, 8 байт |
boolean | 1 | true, false | false | Логический тип (только true & false) |
char | 2 | 0..65,535 | ‘\u0000 ‘ | Символы, 2 байта, все больше 0 |
Object | 4 | Любая ссылка или null. | null | Хранит ссылки на объекты типа Object или классов наследников |
— Давай я остановлюсь на каждом типе подробнее.
— Тип byte – это самый маленький тип целых чисел. Каждая переменная этого типа занимает всего один байт памяти, поэтому он может хранить значения в диапазоне от -128 до 127.
— А зачем такой маленький тип нужен. Почему нельзя везде использовать int?
— Можно. Но если ты создаешь очень большие массивы, и тебе не нужно хранить там значения больше 100, то почему бы не воспользоваться таким типом. Так ведь?
— Тип short – ровно в два раза длиннее типа byte и тоже хранит только целые числа. Самое большое число, которое в него вмещается – это 32767. Самое большое отрицательное число – это -32768.
— Тип int ты уже знаешь. Он может хранить целые числа до двух миллиардов, как положительные, так и отрицательные.
— Тип float – создан для хранения вещественных (дробных) чисел. Его размер 4 байта.
— Все дробные числа хранятся в памяти в очень интересной форме.
— Например, число 987654.321 можно представить как 0.987654321*10 6 . Поэтому в памяти оно будет представлено как два числа «0. 987654321 » (мантисса — значащая часть числа) и « 6 » (экспонента — степень десятки)
— А зачем так сделано?
— Такой подход позволяет хранить гораздо большие числа, чем int, используя всего 4 байта. Но при этом мы жертвуем точностью. Часть памяти расходуется на хранение мантиссы, поэтому такие числа хранят всего 6-7 знаков после запятой, остальные отбрасываются.
—Такие числа еще называют «числа с плавающей запятой» или «числа с плавающей точкой ( float ing point number)». Отсюда, кстати, и название типа – float .
— Тип double – это такой же тип, как и float , только в два раза длиннее – он занимает восемь байт. ( double – двойной, по-английски). И предельный размер мантиссы и количество значащих цифр в нем больше. Если тебе нужно хранить вещественные числа – старайся использовать именно этот тип.
— Тип char – гибридный тип. Его значения можно интерпретировать и как числа (их можно складывать и умножать) и как символы. Так было сделано потому, что хоть символы и имеют визуальное представление, для компьютера они в первую очередь просто числа. И работать с ними как с числами гораздо удобнее. Тут еще есть одно замечание: тип char строго положительный – отрицательных значений он хранить не может.
— Тип boolean – логический тип и может хранить всего два значения: true (истина) и false (ложь).
— Тип Object , хоть и представлен в таблице, примитивным типом не является. Это базовый класс для всех классов в Java. Во-первых, все классы считаются унаследованными от него, а значит, содержат его методы. А во-вторых, ему можно присваивать ссылки на объекты любого типа. В том числе и null – пустую ссылку.
Java/Типы данных
В Java есть 8 примитивных типов, которые делят на 4 группы, вот они:
- Целые числа — byte, short, int, long
- Числа с плавающей точкой (иначе вещественные) — float, double
- Логический — boolean
- Символьный — char
Целочисленные типы [ править ]
Целочисленные типы различаются между собой только диапазонами возможных значений, например, для хранения номера элемента в таблице Менделеева пока хватит переменной типа byte.
Тип | Размер (бит) | Диапазон |
---|---|---|
byte | 8 бит | от -128 до 127 |
short | 16 бит | от -32768 до 32767 |
char | 16 бит | беззнаковое целое число, представляющее собой символ UTF-16 (буквы и цифры) |
int | 32 бит | от -2147483648 до 2147483647 |
long | 64 бит | от -9223372036854775808L до 9223372036854775807L |
Пример использования целочисленных типов:
Символы тоже относят к целочисленным типам из-за особенностей представления в памяти и традиций.
Типы с плавающей точкой [ править ]
Тип | Размер (бит) | Диапазон |
---|---|---|
float | 32 | от 1.4e-45f до 3.4e+38f |
double | 64 | от 4.9e-324 до 1.7e+308 |
Логический тип [ править ]
Тип | Размер (бит) | Значение |
---|---|---|
boolean | 8 (в массивах), 32 (не в массивах используется int) | true (истина) или false (ложь) |
В стандартной реализации Sun JVM и Oracle HotSpot JVM тип boolean занимает 4 байта (32 бита), как и тип int. Однако, в определенных версиях JVM имеются реализации, где в массиве boolean каждое значение занимает по 1-му биту.
Ссылочные [ править ]
Ссылочные типы — это все остальные типы: классы, перечисления и интерфейсы, например, объявленные в стандартной библиотеке Java, а также массивы.
Строки [ править ]
Строки это объекты класса String, они очень распространены, поэтому в некоторых случаях обрабатываются отлично от всех остальных объектов. Строковые литералы записываются в двойных кавычках.
Эта программа выведет:
Hello World
foo == bar ? true
foo равен bar ? true
foo == baz ? false
foo равен baz ? true
Обертки [ править ]
Если требуется создать ссылку на один из примитивных типов данных, необходимо использовать соответствующий класс-обертку. Также в таких классах есть некоторые полезные методы и константы, например минимальное значение типа int можно узнать использовав константу Integer.MIN_VALUE. Оборачивание примитива в объект называется упаковкой (boxing), а обратный процесс распаковкой (unboxing).
Тип | Класс-обертка |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
Рекомендуется использовать valueOf, он может быть быстрее и использовать меньше памяти потому что применяет кэширование, а конструктор всегда создает новый объект.
Получить примитив из объекта-обертки можно методом <имя примитивного типа>Value.
boolean и boolean» Память Layout в JVM
В этой быстрой статье мы увидим, каков след булеан значение в СПМ в различных обстоятельствах.
Во-первых, мы проинспектировать JVM, чтобы увидеть размеры объекта. Тогда мы поймем обоснование этих размеров.
2. Настройка
Для проверки макета памяти объектов в JVM мы собираемся использовать макет объекта Java ( ДЖОЛ ) широко. Поэтому нам нужно добавить jol-core зависимость:
3. Размеры объектов
Если мы попросим JOL распечатать детали VM с точки зрения размеров объектов:
Когда сжатые ссылки включены (поведение по умолчанию), мы увидим выход:
В первых нескольких строках мы можем увидеть некоторую общую информацию о VM. После этого мы узнаем о размерах объектов:
- Java ссылки потребляют 4 байта, булеан s/ byte s 1 byte, char s/ короткие s 2 байта, int s/ поплавок s 4 байта, и, наконец, долго s/ двойной s 8 байтов
- Эти типы потребляют одинаковое количество памяти, даже когда мы используем их в качестве элементов массива
Таким образом, в присутствии сжатых ссылок, каждый булеан значение занимает 1 byte. Аналогичным образом, каждый булеан в булеане потребляет 1 byte. Тем не менее, выравнивание обивки и заготовки объектов может увеличить пространство, потребляемое булеан и булеане как мы увидим позже.
3.1. Нет сжатых ссылок
Даже если мы отключим сжатые ссылки через -XX:-UseCompressedOops , размер boolean не изменит на всех :
С другой стороны, ссылки Java берут в два раза больше памяти.
Так что, несмотря на то, что мы могли бы ожидать в первую очередь, булеаны потребляют 1 тотализатор вместо всего 1 бита.
3.2. Слово Разрыв
В большинстве архитектур нет способа получить доступ к одному биту атоманно. Даже если бы мы хотели сделать это, мы, вероятно, в конечном итоге писать на соседние биты при обновлении другого.
Одной из целей СПМ является предотвращение этого явления, известного как слово разрывая . То есть в СПМ каждая область и элемент массива должны быть отдельными; обновления одного поля или элемента не должны взаимодействовать с чтениями или обновлениями какого-либо другого поля или элемента.
Напомним, вопросы адресности и разрыва слов являются основными причинами, по которым булев s больше, чем просто один бит.
4. Обыкновенные указатели объектов (OOPs)
Теперь, когда мы знаем булеан s 1 byte, рассмотрим этот простой класс:
Если мы проверяем макет памяти этого класса с помощью JOL:
Затем JOL распечатает макет памяти:
BooleanWrapper макет состоит из:
- 12 байтов для заголовка, в том числе два марк слова и один Класс слово. СПМ HotSpot использует марк слово для хранения метаданных GC, хэш-кода идентификации и информации о блокировке. Кроме того, он использует Класс слово для хранения метаданных класса, таких как проверки типа времени выполнения
- 1 тотализатор для фактического булеан ценность
- 3 байта обивки для выравнивания целей
По умолчанию ссылки на объекты должны быть выровнены на 8 байтов. Таким образом, JVM добавляет 3 байта до 13 байтов заголовка и булеан чтобы сделать его 16 байтов.
Поэтому булеан поля могут потреблять больше памяти из-за их выравнивания поля.
4.1. Пользовательское выравнивание
Если мы изменим значение выравнивания до 32 через -XX:ОбъектПовинениеInBytes-32, затем один и тот же макет класса изменяется на:
Как показано выше, JVM добавляет 19 байтов обивки, чтобы сделать размер объекта кратным 32.
5. Массив OOPs
Давайте посмотрим, как JVM излагает булеан массив в памяти:
Это будет печатать макет экземпляра следующим образом:
В дополнение к двум марк слова и один Класс слово, массив указатели содержат дополнительные 4 байта для хранения их длины.
Так как наш массив имеет три элемента, размер элементов массива составляет 3 байта. Тем не менее, эти 3 байта будут проложены 5 байтами выравнивания поля для обеспечения надлежащего выравнивания.
Хотя каждый булеан элемент в массиве составляет всего 1 тотализатор, весь массив потребляет гораздо больше памяти. Другими словами, мы должны рассмотреть заголовок и обивка накладных расходов при вычислении размера массива.
6. Заключение
В этом быстром учебнике, мы увидели, что булеан поля потребляют 1 byte. Кроме того, мы узнали, что мы должны рассмотреть заголовок и обивка накладных расходов в размерах объектов.
Для более подробного обсуждения настоятельно рекомендуется проверить Упс раздел СПМ исходный код. Кроме того, Алексей Шипилов имеет гораздо более углубленные статьи в этой области.