Перейти к содержимому

Какая размерность у boolean java

  • автор:

Размер типа boolean

Для всех примитивных типов данных, кроме типа boolean , в Java однозначно определен размер. Размер типа boolean , насколько я понимаю, зависит от реализации конкретной виртуальной машины. Вопрос: почему для типа boolean однозначно не определили размер?

Отслеживать
задан 8 фев 2017 в 23:39
417 1 1 золотой знак 4 4 серебряных знака 11 11 бронзовых знаков

Встречный вопрос. А с какими целями это, по-вашему, может быть надо? Полагаю, что ответ «определять размер однозначно было просто незачем» вас не устроит.

– user181100
8 фев 2017 в 23:48
@D-side, Это же ровно то, что я спрашиваю.
8 фев 2017 в 23:50

Потому что boolean — 1 бит. А адресовать один бит нельзя. А количество бит/байт которые можно адресовать (читай, минимальный размер памяти, который можно прочитать) в теории зависит от реальной машины, на которой исполняется java. Думаю причина в этом.

8 фев 2017 в 23:53

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

А зачем? Языку не должно быть до этого дела.

Если поведение однозначно определено, всё остальное вторично.

Тип boolean прекрасно определяется множеством своих допустимых значений. Математически оно ограничивает минимальный размер значения одним битом.

Но использовать именно один бит (и ни битом более) эффективно далеко не всегда, поскольку на популярных архитектурах нельзя адресовать отдельные биты. А потому значения отдельных boolean ‘ов нельзя быстро сохранить в отдельные биты оперативной памяти — необходимо использовать комбинацию побитовых операций, что почти наверняка будет медленнее, чем запись целого отдельного регистра в оперативную память (размер которого может быть различным на разных платформах!).

Но при этом, к примеру, я вполне себе представляю, как на x86 компилятор может использовать в нативном коде в качестве какого-нибудь конкретного boolean -значения один бит регистра флагов, при условии, что это значение никогда не попадает в оперативную память (отдельно от других). Поэтому любые ограничения на размер более одного бита тоже могут потенциально мешать.

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

Тайны самого простого типа в Java

На одном из собеседований мне задали вопрос: так, сколько байт занимает переменная типа boolean в памяти? А типа Boolean?

Я, не долго думая, выпалил, что, мол о каких байтах речь, наверняка boolean в Java занимает 1 БИТ, а восемь флажков так и вообще 1 БАЙТ.

Мне сказали, что я не прав, в Java все иначе и вообще, идите, учите матчасть.

Типы в Java и их размеры

Я думаю вы часто встречали что-то вроде такого:

Type Size in Bytes Range
byte 1 byte -128 to 127
short 2 bytes -32,768 to 32,767
int 4 bytes -2,147,483,648 to 2,147,483, 647
long 8 bytes -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
float 4 bytes approximately ±3.40282347E+38F (6-7 significant decimal digits) Java implements IEEE 754 standard
double 8 bytes approximately ±1.79769313486231570E+308 (15 significant decimal digits)
char 2 byte 0 to 65,536 (unsigned)
boolean not precisely defined* true or false

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

Согласно официальному туториалу от Sun/Oracle мы видим следующую картину для народных масс: boolean представляет 1 бит информации, но размер остается на совести того, кто воплощает спеку JVM.

Заглядывая в спеку JVM

Собственно в спеке, на одной из первых страниц [стр.20], мы видим приписку, что, мол boolean в ранних спеках и за тип не считался, настолько он специфический. Впрочем, параграф 2.3.4, приоткрывает завесу тайны над идеями имплементации boolean на конкретной виртуальное машине.

There are no Java Virtual Machine instructions solely dedicated to operations on boolean values. Instead, expressions in the Java programming language that operate on boolean values are compiled to use values of the Java Virtual Machine int data type.

Т.е. нам ясно говорят, что в целом boolean внутренне — это типичный 4-байтовый int. Соответственно, переменная типа boolean, скорее всего будет занимать 4 байта (в 32 раза больше, чем само значение, которое она презентует).

Проверяя bytecode

Напишем простой пример

public class Sample < public static void main(String[] args) < boolean flag = true; flag = false; > > 

и поглядим в его bytecode

// class version 52.0 (52) // access flags 0x21 public class experiment/Sample < // compiled from: Sample.java // access flags 0x1 public ()V L0 LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object. ()V RETURN L1 LOCALVARIABLE this Lexperiment/Sample; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 5 L0 ICONST_1 ISTORE 1 L1 LINENUMBER 6 L1 ICONST_0 ISTORE 1 L2 LINENUMBER 7 L2 RETURN L3 LOCALVARIABLE args [Ljava/lang/String; L0 L3 0 LOCALVARIABLE flag Z L1 L3 1 MAXSTACK = 1 MAXLOCALS = 2 > 

Мы видим тут замечательные ICONST_1/ICONST_0 — это специальные инструкции для JVM, чтобы положить на стек 1 и 0, соответственно. Т.е. good-old true/false превращаются в 1 и 0. А нам еще запрещают в java писать выражения ‘true + 1’

INT? Ну серьезно? Почему не short или byte? Почему огромный многословный int? Это вызывает вопросы, на которые я попробую ответить ближе к концу статьи.

А массивы?

Кажется, что должно быть все грустно. Массив из десятка boolean будет занимать столько же места, что и массив из десятка int-ов. Как бы не так!

В той же спеке, в параграфе 2.3.4, говорится

The Java Virtual Machine does directly support boolean arrays. Its newarray instruction (§newarray) enables creation of boolean arrays. Arrays of type boolean are accessed and modified using the byte array instructions baload and bastore (§baload, §bastore).

Поглядим байткод для одного такого массива с парочкой изменяемых элементов.

 boolean[] flags = new boolean[100000]; flags[99999] = false; flags[88888] = true && false; 
// class version 52.0 (52) // access flags 0x21 public class experiment/Sample < // compiled from: Sample.java // access flags 0x1 public ()V L0 LINENUMBER 5 L0 ALOAD 0 INVOKESPECIAL java/lang/Object. ()V RETURN L1 LOCALVARIABLE this Lexperiment/Sample; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 9 L0 LDC 100000 NEWARRAY T_BOOLEAN ASTORE 1 L1 LINENUMBER 10 L1 ALOAD 1 LDC 99999 ICONST_0 BASTORE L2 LINENUMBER 11 L2 ALOAD 1 LDC 88888 ICONST_0 BASTORE L3 LINENUMBER 14 L3 RETURN L4 LOCALVARIABLE args [Ljava/lang/String; L0 L4 0 LOCALVARIABLE flags [Z L1 L4 1 MAXSTACK = 3 MAXLOCALS = 2 > 

Ура, новые операции видны. Впрочем наши любимые ICONST_0 никто не отменял.

Там же идет мелким шрифтом, как часть контракта, которую никто не читает

In Oracle’s Java Virtual Machine implementation, boolean arrays in the Java programming language are encoded as Java Virtual Machine byte arrays, using 8 bits per boolean element.

Грубо говоря, парни, на правильных JVM все не так плохо и мы можем сэкономить на счетах за электричество.

Уже британскими учеными разработаны новейшие инструкции для загрузки и выгрузки значений в такой массив, да и сам массив будет задействовать только по 1 байту на элемент.

Ну что, неплохо, но надо проверять.

Я давно не доверяю методам замера памяти а-ля Runtime.getRuntime().freeMemory(), поэтому я воспользовался библиотечкой JOL из состава OpenJDK.

  dependency> groupId>org.openjdk.jol groupId> artifactId>jol-core artifactId> version>0.8 version> dependency> 

Там есть простая возможность узнать размеры элемента массива для вашей конкретной JVM

 System.out.println(VM.current().details()); 

Результат оказался ожидаемым, наш элемент и вправду занимает 1 байт на моей машине вместе с Oracle JDK 1.8.66

# Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift. # Objects are 8 bytes aligned. # Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] 
А что насчет типа Boolean?

Вот уж кто, наверное, жрет памяти за обе щеки.

А вот и нет, вполне себе скромный честный труженик с тратой памяти на заголовок и выравнивание

header: 8 bytes value: 1 byte padding: 7 bytes ------------------ sum: 16 bytes 
Что же делать?

Ну если вам нужен именно тип boolean, он у вас во всех сигнатурах и т.д. — то ничего не делать, сидеть на попе ровно и ждать 50 релиза Java, может там престарелый Леша Шиппелoff научится распихивать свои стринги по карманам boolean.

Если дело только в эффективных структурах данных, то даже в самой Java есть кое-что на закуску. Это https://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html

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

import java.util.BitSet; public class BitSetDemo < public static void main(String args[]) < BitSet bits1 = new BitSet(16); BitSet bits2 = new BitSet(16); // set some bits for(int i = 0; i < 16; i++) < if((i % 2) == 0) bits1.set(i); if((i % 5) != 0) bits2.set(i); > System.out.println("Initial pattern in bits1: "); System.out.println(bits1); System.out.println("\nInitial pattern in bits2: "); System.out.println(bits2); // AND bits bits2.and(bits1); System.out.println("\nbits2 AND bits1: "); System.out.println(bits2); // OR bits bits2.or(bits1); System.out.println("\nbits2 OR bits1: "); System.out.println(bits2); // XOR bits bits2.xor(bits1); System.out.println("\nbits2 XOR bits1: "); System.out.println(bits2); > > 
Initial pattern in bits1: Initial pattern in bits2: bits2 AND bits1: bits2 OR bits1: bits2 XOR bits1: <> 
Почему сразу не сделали хорошо?

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

Во-вторых, и byte и short не являются полноценными численными типами, над ними довлеет проклятие int и его целочисленных операций, вряд ли бы мы смогли существенно сэкономить, перегоняя boolean->byte->int

results matching » «

No results matching » «

Почему boolean использует больше памяти чем byte?

В таблицах типов данных написано что boolean использует 2 байта, когда byte 1 байт. boolean это вроде 1 и 0, true и false,
а byte это 0-255, тогда почему он использует меньше памяти?

  • Вопрос задан 04 июн. 2023
  • 861 просмотр

18 комментариев

Простой 18 комментариев

В каких таблицах? Какой язык программирования?

Deita

boolean использует 2 байта, когда byte 1 байт.

это в каком языке программирования по вашему так сделано и на какой архитектуре вы об Python? Вы можете конкретизировать?

Думаю что он занимает столько места так как является ссылкой на константу

phaggi

byte это 0-255

это платформозависимое значение.

Steel_Balls

Steel_Balls @Steel_Balls

а byte это 0-255,

Нет, это не так.
в некоторых языках байт имеет 16 бит, а в Java, например, byte принимает значения -128. +127
https://docs.oracle.com/javase/8/docs/api/java/lan.

vabka

Steel_Balls, в каких языках байт равен 16 бит?

Steel_Balls

Steel_Balls @Steel_Balls
Василий Банников, я тебе больше скажу — бит иногда может иметь три значения, а не привычные два

vabka

Steel_Balls, всё ещё жду какой-нибудь живой пример)

phaggi

Василий Банников, не в языках, а в платформах или конкретных моделях ЭВМ. Почитайте про байт на Википедии, там есть примеры.
Сейчас байт как правило 8-битный, но это так сложилось, поддерживается для совместимости, но нигде в стандартах байт не ограничен конкретным числом ни сверху ни снизу.

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

Steel_Balls

Steel_Balls @Steel_Balls

Василий Банников, живой пример был у меня на работе лет 10 назад. Дедушка-программист 70-ти лет.
Невероятно интересный экземпляр. Ох, сколько же он нам много интересного рассказал про советские ЭВМ, на которых работал в 60-70-е годы и до сих пор программит.
И да, это от него я узнал про бит с тремя значениями, т.к. он лично работал на такой машине. И про байт он тоже развеял мои мифы

Steel_Balls

Steel_Balls @Steel_Balls

в каких языках байт равен 16 бит?

Байт может состоять даже из 4 бит.
8 бит — это просто привычное значение, но оно не обязано быть таковым.

Steel_Balls

Steel_Balls @Steel_Balls

Василий Банников, википедия:
Однако в истории компьютерной техники существовали решения с иными размерами байта (например, 6, 32 или 36 бит), поэтому иногда в компьютерных стандартах и официальных документах для однозначного обозначения группы из 8 бит используется термин «октет» (лат. octet).

vabka

Steel_Balls, какая буква в слове «живой» вам не понятна?)

Ну и речь не о людях, а о железе. Для чего реально придётся писать код

vabka

е в языках, а в платформах или конкретных моделях ЭВМ. Почитайте про байт на Википедии, там есть примеры.

Я в курсе)
Но каким боком древние архитектуры относятся к текущему вопросу?
Вот эта вся философия о том что байт не обязательно 8 бит и что бит не обязательно двоичный — это полемика, которая к вопросу, кмк, отношения не имеет

phaggi

Василий Банников, в интернете всегда кто-нибудь не прав!

vabka

Алан Гибизов, ужас вообще да

Steel_Balls

Steel_Balls @Steel_Balls

Василий Банников, я программист и привык всегда требовать уточнения задачи.
Профдеформация. Вам меня не понять

Deita

Steel_Balls, начинаю кажется уже привыкать к тому, что программирование начинают учить не с чистого Си а с Питона

Решения вопроса 3
mayton2019 @mayton2019
Bigdata Engineer

С точки зрения ассемблера — удобнее делать операции над целым числом (DWORD) 32bit. Он — же int.
В масштабах современной памяти — безразлично выделять 1 байт или 4 байта. Все равно
другие структуры больше захватят тысячекратно. А простота компилируемого кода — здесь важнее.

Вот поэтому компилляторы и собирают boolean в int. А язык С++ позволяет проводить аналогии
между int и boolean. В зависимости от контекста число может быть как числом так и логической
величиной. (Тесная связь с ассемблером да).

А если речь допустим идет о массиве boolean — тогда смысл в экономии есть. В Java например
такой массив действительно будет использовать биты внутри байта.

В базах данных например (Oracle) вообще нет булевого типа с точки зрения хранения. Там предлагают
символьный тип ‘Y’ или ‘N’. Это связано с дисковыми структурами хранения данных в datarow.

Поэтому sizeof(булевый тип) будет всегда парадоксом в разных системах программирования и языках.
Как компиллятору удобнее так и соберет.

Ответ написан 04 июн. 2023
Комментировать
Нравится 6 Комментировать

vabka

Токсичный шарпист

1. Это очень зависит от конкретного языка.
2. А ещё и от деталей реализации зависит. В некоторых языках даже может быть не определено, сколько байт занимает bool и он может занимать от 1 байта (меньше — только если реализовать его как битовый флаг в структуре побольше) до 8 байт (чтобы попасть в выравнивание)

А byte всегда равен одному байту, тк ему реально важен размер.

Ответ написан 04 июн. 2023
Комментировать
Нравится 2 Комментировать
Saboteur @saboteur_kiev
software engineer

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

Можно сделать кастомную реализацию с побитовыми операциями, тогда можно будет засунуть восемь boolean переменных в один байт, но вряд ли есть хоть один язык программирование, где такое будет сделано штатно, ибо это неудобно.
Читайте доку про ваш язык программирования

Ответ написан 04 июн. 2023
Нравится 2 2 комментария

Можно сделать кастомную реализацию с побитовыми операциями, тогда можно будет засунуть восемь boolean переменных в один байт, но вряд ли есть хоть один язык программирование, где такое будет сделано штатно

Есть такое. В С++ std::vector именно так и реализован:https://en.cppreference.com/w/cpp/container/vector_bool
Для него в std написана специализация.

Saboteur @saboteur_kiev

Просто в любом случае у вас будет как минимум один байт занят, и выгода в том, что 8 boolean там будет храниться.
С точки зрения быстродействия наверное выгоды никакой..

Java/Типы данных

В Java есть 8 примитивных типов, которые делят на 4 группы, вот они:

  1. Целые числа — byte, short, int, long
  2. Числа с плавающей точкой (иначе вещественные) — float, double
  3. Логический — boolean
  4. Символьный — char

Целочисленные типы [ править ]

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

Тип Размер (бит) Диапазон
byte 8 бит от -128 до 127
short 16 бит от -32768 до 32767
char 16 бит беззнаковое целое число, представляющее собой символ UTF-16 (буквы и цифры)
int 32 бит от -2147483648 до 2147483647
long 64 бит от -9223372036854775808L до 9223372036854775807L

Пример использования целочисленных типов:

public class IntegralTypes  public static void main(String[] args)  byte b = 216; // Вот тут будет ошибка, т.к. у нас диапазон от -128 до 127! short s = 1123; int i = 64536; long l = 2147483648L; // Постфикс l или L обозначает литералы типа long System.out.println(i); System.out.println(b); System.out.println(s); System.out.println(l); > > 

Символы тоже относят к целочисленным типам из-за особенностей представления в памяти и традиций.

public class Characters  public static void main(String[] args)  char a = 'a', b, c = 'c'; b = (char) ((a + c) / 2); // Можно складывать, вычитать, делить и умножать // Но из-за особенностей арифметики Java результат приходится приводить к типу char явно System.out.println(b); // Выведет символ 'b' > > 

Типы с плавающей точкой [ править ]

Тип Размер (бит) Диапазон
float 32 от 1.4e-45f до 3.4e+38f
double 64 от 4.9e-324 до 1.7e+308
public class FloatingPointTypes  public static void main(String[] args)  double a, b = 4.12; a = 22.1 + b; float pi = 3.14f; // При использовании типа float требуется указывать суффикс f или F // так как без них типом литерала будет считаться double float anotherPi = (float) 3.14; // Можно привести явно double c = 27; double d = pi * c; System.out.println(d); > > 

Логический тип [ править ]

Тип Размер (бит) Значение
boolean 8 (в массивах), 32 (не в массивах используется int) true (истина) или false (ложь)

В стандартной реализации Sun JVM и Oracle HotSpot JVM тип boolean занимает 4 байта (32 бита), как и тип int. Однако, в определенных версиях JVM имеются реализации, где в массиве boolean каждое значение занимает по 1-му байту.

Ссылочные [ править ]

Ссылочные типы — это все остальные типы: классы, перечисления и интерфейсы, например, объявленные в стандартной библиотеке Java, а также массивы.

Строки [ править ]

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

public class Strings  public static void main(String[] args)  String a = "Hello", b = "World"; System.out.println(a + " " + b); // Здесь + означает объединение (конкатенацию) строк // Пробел не вставляется автоматически // Строки конкатенируются слева направо, надо помнить это когда соединяешь строку и примитив String c = 2 + 2 + ""; // "4" String d = "" + 2 + 2; // "22" d = "" + (2 + 2); // а теперь d тоже "4" String foo = "a string"; String bar = "a string"; // bar будет указывать на тот же объект что и foo String baz = new String("a string"); // Чтобы гарантированно создать новую строку надо вызвать конструктор System.out.println("foo == bar ? " + (foo == bar)); // == сравнивает ссылки на объекты System.out.println("foo равен bar ? " + (foo.equals(bar))); // Метод equals служит для проверки двух объектов на равенство System.out.println("foo == baz ? " + (foo == baz)); System.out.println("foo равен baz ? " + (foo.equals(baz))); > > 

Эта программа выведет:
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
int i; Integer boxed; // Обычное создание объекта boxed = new Integer(i); // Фабричный метод boxed = Integer.valueOf(i); // Автоматическая упаковка, компилятор просто вставит вызов Integer.valueOf boxed = i; 

Рекомендуется использовать valueOf, он может быть быстрее и использовать меньше памяти потому что применяет кэширование, а конструктор всегда создает новый объект.

Получить примитив из объекта-обертки можно методом Value.

Integer boxed; int i; // Явная распаковка i = boxed.intValue(); // Автоматическая распаковка i = boxed; 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *