Размер типа 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 » «
Руководство по Java Core. Типы данных.
Переменные – это зарезервированное место в памяти для хранения значения. Другими словами, когда мы объявляем переменную, мы резервируем место в памяти.
В зависимости от типа переменной (целые числа, строки, байте и т.д.) выделяется определённое количество памяти.
В языке Java все типы данных делятся на две большие группы:
- Примитивные типы данных
- Ссылочное типы данных (Объекты)
Примитивные типы данных
В Java есть 8 примитивных типов данных:
byte
- 8-битное целое число
- Максимальное значение: -128 (-2 ^ 7)
- Минимальное значение: 127 (2 ^ 7 -1)
- Значение по умолчанию: 0
- Используется для экономии места в больших массивах. Чаще всего вместо int.
- Пример: byte c = 65
short
- 16-битное целое число
- Максимальное значение: -32,768 (-2 ^ 15)
- Минимальное значение: 32,767 (2 ^ 15 – 1)
- Значение по умолчанию: 0
- Используется для экономии места вместо int.
- Пример: short a = 20000, short b = -10000
int
- 32-битное целое число
- Максимальное значение: -2,147,483,648 (-2 ^ 31)
- Минимальное значение: 2,147,483,647 (-2 ^ 31 – 1)
- Значение по умолчанию: 0
- Используется для целых значений в случае, если нет дефицита памяти.
- Пример: int i = 2000000000, int h = -1500000000
long
- 64-битное целое число
- Максимальное значение: -9,223,372,036,854,775,808 (-2 ^ 63)
- Минимальное значение: 9,223,372,036,854,775,807 (-2 ^ 63 – 1)
- Значение по умолчанию: 0L
- Используется для хранения больших целочисленных значений.
- Пример: long l = 5000000000L, long k = -4000000000L
float
- 32-битное число с плавающей точкой IEEE 754
- Значение по умолчанию: 0.0f
- Используется для экономии памяти в больших массивах чисел с плавающей точкой.
Никогда не используется для хранения точных значений (например, денег). - Пример: float f = 112.3f
double
- 64-битное число двойной точности с плавающей точкой IEEE 754
- Значение по умолчанию: 0.0d
- Используется для хранения чисел с плавающей точкой (в большинстве случаев).
- Никогда не используется для хранения точных значений (например, денег).
- Пример: double d = 121.5
boolean
- В спецификации размер не указан. Зависит от типа JVM.
- Возможные значения: true/false
- Значение по умолчанию: false
- Используется для определения того, является ли условие истинным.
- Пример: boolean flag = true
char
- Символ кодировки Unicode 16-bit
- Максимальное значение: ‘\u0000’ (или 0)
- Минимальное значение: ‘uffff’ (или 65.535)
- Используется для хранения любого символа
- Пример: char c = ‘C’
Ссылочные типы данных
- К ссылочным типам данных относятся все типы данных, которые создаются с помощью конструкторов. К ним также относятся все классы, создаваемые разработчиками, например, Developer, Car, Person и т.д.
- Массивы являются ссылочными типами данных.
- Ссылочная переменная может использоваться в качестве ссылки на любой объект определённого типа данных.
- Все ссылочные типы имеют значение по умолчанию: null.
- Пример:
Developer developer = new Developer(“Java Developer”);
Литералы
Литералы – это представление фиксированных значений в виде кода. Они не требуют каких-либо вычислений. Например:
[code lang=”java”]
char c = ‘C’
[/code]
Значения int, long, short, byte могут быть выражены с помощью десятичной, шестнадцатеричной и восьмеричной систем.
Пример:
[code lang=”java”]
int decimal = 500;
int octal = 0168;
int hexa = 0x32;
[/code]
Для строк в языке Java используется класс String. String может содержать как простые символы, так и символы Unicode:
[code lang=”java”]
char c = ‘\uffff’;
String str = “\uffff”;
[/code]
В языке Java также существует ряд управляющих последовательностей для отображения некоторых символов:
\n | Новая строка (0x0a) |
\r | Возврат каретки (0x0d) |
\f | Прогон страницы (0x0c) |
\b | Возврат на символ назад (0x08) |
\s | Пробел (0x20) |
\t | Табуляция |
\” | Двойная кавычка |
\’ | Одинарная кавычка |
\\ | Обратный слэш |
\xxx | Восьмеричный символ (xxx) |
\uxxxx | Шестнадцатеричный символ UNICODE (xxxx) |
В этом уроке мы изучили основные типы данных в языке Java и литералы.
В следующем уроке мы рассмотрим различные типы переменных и примеры их использования.
Полезности
Туториалы
Системный дизайн
Собеседования
Студенты
Задачи
Немного о себе
Приветствую! Меня зовут Евгений. На этом сайте я пишу о разработке программного обеспечения. Связаться со мной вы можете по email: proselytear@yahoo.com Имеет смысл, предварительно ознакомиться вот с этим FAQ разделом.
Недавние публикации
- Механизмы CAS и FAA глазами Java разработчика
- ExecutorService в Java и примеры его применения.
- Особенности работы PreparedStatement в JDBC
- Основы кэширования в Hibernate
- Феномены чтения глазами разработчика
Copyright © 2023 PROSELYTE.
Omega WordPress Theme by ThemeHall
boolean — боремся за Java память…
У Вас когда-либо случались такие ситуации, когда Ваше Java приложение трещит по швам? В моём случае это случилось из-за нехватки доступной оперативной памяти. И, естественно, обнаружилась нехватка в самый неподходящий момент: на носу очередной долгожданный релиз, один из серверов остановлен для обновления кода и данных и реинкарнация старого кода уже невозможна, в ближайшие дни запланировано несколько совещаний и собеседований, что сильно отвлекает от процесса оптимизации — в общем, ЧП не прошло незамеченным.
К слову сказать, сделай я правильный backup и экстренные работы по восстановлению жизнеспособности прошли бы гораздо более спокойно, но это была бы уже совсем другая история. Итак в моём распоряжении есть код, которому не хватает 15Gb оперативной памяти для нормального функционирования и очень длительный и дорогостоящий процесс запуска (около 5 часов), в ходе работы которого можно только сидеть со скрещенными пальцами и надеятся, что в этот раз заветные слова OutOfMemoryException не появятся в консоли удалённого сервера.
Не буду описывать всех ухищрений, которые пришлось проделать, чтобы восстановить остановленный сервер в течении трёх дней, но одним своим мини открытием поделюсь — boolean — это не тот тип данных, который Вы хотите использовать в высоконагруженных системах. Внимание вопрос:
Как Вы думаете, сколько памяти занимает boolean например на Ubuntu server x64?
Правильным ответом будет: неизвестно и зависит только от реализации JVM.
Рассмотрим распространённую Sun JVM и прочтем в спецификации виртуальной машины, что boolean типа в ней нет как такового, вместо него используется int! А это означает, что для хранения значения типа «да\нет» используется ровно 32 бита, независимо от архитектуры процессора. Правда в том же разделе мы видим, что произведена оптимизация для работы с массивами boolean, которые преобразуются в массив байт, что даёт прирост доступной памяти в 4 раза. И всё же платить за хранение нолика или еденички семью лишними битами — иногда просто кощунство и издевательство над серверами (особенно при размерах массивов в 500 миллионов элементов).
Спасением в таких случаях будет класс BitSet, который ведёт себя подобно массиву boolean, но упаковывает данные так, что для одного бита выделяется всего один бит памяти (с небольшими издержками для всего массива). BitSet хранит внутри себя массив типа long, а при запросе или установке значения определенного бита — высчитывает индекс нужного long и пользуясь побитовыми операциями и операциями сдвига производит вычисления над единственным битом.
Существует еще более интересная реализация BitSet, OpenBitSet — Apache реализация, которая используется для Lucene. Она гораздо быстрее, но упускает некоторые проверки, проводимые в оригинальном BitSet. Что использовать — решать Вам.