Чому булівський примітивний розмір Java не визначений?


111

Специфікація віртуальної машини Java говорить про обмежену підтримку булевих примітивних типів.

Немає інструкцій віртуальної машини Java, присвячених виключно операціям з булевими значеннями. Натомість вирази в мові програмування Java, які працюють на булевих значеннях, складаються для використання значень типу даних int віртуальної машини Java.

Вищезазначене означає (хоча я, можливо, його неправильно трактував), що тип даних 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

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

1
Іноді упаковка булів може бути насправді швидшою. Щоразу, коли має значення розмір кеша, може бути краще упакувати речі. Наприклад, сито, яке сегментоване, працює в шматках 32 кБ (розмір кешу L1) набагато швидше, ніж несегментоване сито. Між шматками є кілька накладних витрат, і за упаковку ви платите накладні витрати у вісім разів рідше. Я його ще не вимірював.
maaartinus

7

Один булевий файл десь у ієрархії спадкування може використовувати до 8 байт! Це пов’язано з оббивкою. Більш детальну інформацію можна знайти в розділі Скільки пам'яті використовує мій об’єкт Java? :

Повертаючись до питання про те, скільки споживає булевий, так, він споживає хоча б один байт, але завдяки правилам вирівнювання він може споживати набагато більше. IMHO цікавіше знати, що булевий [] буде споживати один байт на запис, а не один біт, плюс деякий накладний рахунок за рахунок вирівнювання та для поля розміру масиву. Існують графічні алгоритми, в яких корисні великі поля біт, і вам потрібно знати, що якщо ви використовуєте булевий [], вам потрібно майже рівно в 8 разів більше пам’яті, ніж дійсно потрібно (1 байт проти 1 біта).


Як би в будь-якому разі використовувати булевий []?
Томас Юнг

булева [] може бути використана для маски. Іноді BitSet може бути кращим, оскільки він має деякі корисні методи.
Майкл Мансі

5

У 5-му виданні Java в горішці (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

Boolean являє собою один біт інформації, але його "розмір" - це не те, що точно визначено, скажімо, навчальні програми 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.