Warum ist die boolesche Grundgröße von Java nicht definiert?

111

Die Java Virtual Machine-Spezifikation besagt, dass boolesche primitive Typen nur eingeschränkt unterstützt werden.

Es gibt keine Anweisungen für Java Virtual Machine, die ausschließlich für Operationen mit booleschen Werten vorgesehen sind. Stattdessen werden Ausdrücke in der Java-Programmiersprache, die mit booleschen Werten arbeiten, kompiliert, um Werte des Datentyps Java Virtual Machine Int zu verwenden.

Das Obige impliziert (obwohl ich es möglicherweise falsch interpretiert habe), dass der Datentyp int verwendet wird, wenn mit Booleschen Werten gearbeitet wird, dies ist jedoch ein 32-Bit-Speicherkonstrukt. Vorausgesetzt, ein Boolescher Wert repräsentiert nur 1 Informationsbit:

  • Warum wird ein Byte- oder Kurztyp nicht als Proxy für einen Booleschen Wert anstelle von int verwendet?
  • Was ist für eine bestimmte JVM die zuverlässigste Methode, um genau herauszufinden, wie viel Speicher zum Speichern eines Booleschen Typs verwendet wird?
Joel
quelle

Antworten:

116

Kurze Antwort: Ja, Boolesche Werte werden als 32-Bit-Entitäten bearbeitet, aber Arrays von Booleschen Werten verwenden 1 Byte pro Element.

Längere Antwort: Die JVM verwendet eine 32-Bit-Stapelzelle, in der lokale Variablen, Methodenargumente und Ausdruckswerte gespeichert sind. Grundelemente, die kleiner als 1 Zelle sind, werden ausgefüllt, Grundelemente, die größer als 32 Bit (lang und doppelt) sind, benötigen 2 Zellen. Diese Technik minimiert die Anzahl der Opcodes, hat jedoch einige besondere Nebenwirkungen (wie die Notwendigkeit, Bytes zu maskieren).

In Arrays gespeicherte Grundelemente verwenden möglicherweise weniger als 32 Bit, und es gibt verschiedene Opcodes zum Laden und Speichern von Grundelementwerten aus einem Array. Boolesche und Byte-Werte verwenden beide den baloadund den bastoreOpcode, was bedeutet, dass Boolesche Arrays 1 Byte pro Element benötigen.

Was das speicherinterne Objektlayout betrifft, so wird dies von den Regeln für die "private Implementierung" abgedeckt. Es kann 1 Bit, 1 Byte oder, wie ein anderes Poster feststellt, an einer 64-Bit-Doppelwortgrenze ausgerichtet sein. Höchstwahrscheinlich wird die grundlegende Wortgröße der zugrunde liegenden Hardware (32 oder 64 Bit) verwendet.


Was die Minimierung des von Booleschen verwendeten Speicherplatzes angeht: Für die meisten Anwendungen ist dies kein Problem. Stapelrahmen (die lokale Variablen und Methodenargumente enthalten) sind nicht sehr groß, und im großen Schema ist ein diskreter Boolescher Wert in einem Objekt auch nicht so groß. Wenn Sie viele Objekte mit vielen Booleschen Werten haben, können Sie Bitfelder verwenden, die über Ihre Getter und Setter verwaltet werden. Sie zahlen jedoch eine Strafe in der CPU-Zeit, die wahrscheinlich größer ist als die Strafe im Speicher.

kdgregory
quelle
Stimmt es für Mitglieder der Booleschen / Byte-Klasse auch, dass sie ebenfalls 4 Bytes sind? Die Klasseninstanz wird als Ganzes auf dem Stapel zugewiesen, daher kann ich mir vorstellen, dass JVM wahrscheinlich 1 Byte pro Boolesches / Byte-Mitglied verwenden und schließlich eine 4-Byte-Ausrichtung für die gesamte Klasseninstanz vornehmen sollte. Ist es so? (Wenn Sie Referenzen haben, die dies beweisen, teilen Sie sie bitte mit)
dma_k
@dma_k: Wie in meiner Antwort erwähnt, ist das Layout einer Klasseninstanz implementierungsabhängig. Beachten Sie jedoch, dass Klasseninstanzen nicht im Stapel gespeichert sind, sondern auf dem Heap (obwohl Sie einige Verweise auf JDK 7 "Escape-Analyse" sehen, mit der Objekte von Stapel zu Heap verschoben werden, scheint dies nicht der Fall zu sein. siehe java.sun.com/javase/7/docs/technotes/guides/vm/…)
kdgregory
1
Manchmal ist das Packen von Booleschen Werten tatsächlich schneller. Wann immer die Cache-Größe wichtig ist, ist es möglicherweise besser, Dinge zu packen. Beispielsweise ist ein segmentiertes Hauptsieb, das in Blöcken von 32 kB (L1-Cache-Größe) arbeitet, viel schneller als ein nicht segmentiertes Sieb. Zwischen den Brocken liegt ein gewisser Aufwand, und beim Packen zahlen Sie den Aufwand achtmal seltener. Ich habe es noch nicht gemessen.
Maaartinus
7

Ein einzelner Boolescher Wert irgendwo in der Vererbungshierarchie kann bis zu 8 Bytes verwenden! Dies liegt an der Polsterung. Weitere Details finden Sie unter Wie viel Speicher wird von meinem Java-Objekt verwendet? ::

Um auf die Frage zurückzukommen, wie viel ein Boolescher Wert verbraucht, ja, er verbraucht zwar mindestens ein Byte, aber aufgrund von Ausrichtungsregeln kann er viel mehr verbrauchen. IMHO ist es interessanter zu wissen, dass ein Boolescher Wert [] ein Byte pro Eintrag und nicht ein Bit verbraucht, zuzüglich eines gewissen Overheads aufgrund der Ausrichtung und des Größenfelds des Arrays. Es gibt Graph-Algorithmen, bei denen große Bitfelder nützlich sind, und Sie müssen sich bewusst sein, dass Sie bei Verwendung eines Booleschen [] fast genau achtmal mehr Speicher benötigen als tatsächlich benötigt (1 Byte gegenüber 1 Bit).

akuhn
quelle
Wie würde man überhaupt einen Booleschen Wert [] verwenden?
Thomas Jung
boolean [] könnte für eine Maske verwendet werden. Manchmal kann ein BitSet jedoch besser sein, da es einige nützliche Methoden enthält.
Michael Munsey
5

Die 5. Ausgabe von Java in Kürze (O'Reilly) besagt, dass ein boolescher primitiver Typ 1 Byte ist. Das könnte falsch sein, basierend auf dem, was die Untersuchung des Haufens zeigt. Ich frage mich, ob die meisten JVMs Probleme haben, Variablen weniger als ein Byte zuzuweisen.

Matthew Flynn
quelle
3

Die boolesche Zuordnung wurde unter Berücksichtigung einer 32-Bit-CPU durchgeführt. Der int-Wert hat 32 Bit, sodass er in einer Operation verarbeitet werden kann.

Hier ist eine Lösung aus Peter Norvigs Java IAQ: Selten beantwortete Fragen zum Messen der Größe (mit etwas Ungenauigkeit):

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.");
Thomas Jung
quelle
Da es sich bei dieser Konversation um Grundelemente handelt, müssen Sie dies kreativ testen, da Grundelemente nur dann im Heap gespeichert werden, wenn sie ein Feld in einer Instanz oder einem Array sind. Und keiner von beiden beantwortet die Frage, wie Java es trotzdem im Stapel speichern wird.
Jesse
2

CPUs arbeiten mit einer bestimmten Datentyplänge. Bei 32-Bit-CPUs sind sie 32 Bit lang und daher in Java als "int" bezeichnet. Alles, was darunter oder darüber liegt, muss auf diese Länge gefüllt oder aufgeteilt werden, bevor die CPU es verarbeiten kann. Dies nimmt nicht viel Zeit in Anspruch, aber wenn Sie für grundlegende Vorgänge 2 CPU-Zyklen anstelle von 1 benötigen, bedeutet dies doppelte Kosten / Zeit.

Diese Spezifikation ist für 32-Bit-CPUs vorgesehen, damit sie Boolesche Werte mit ihrem nativen Datentyp verarbeiten können.

Sie können hier nur eine haben: Geschwindigkeit oder Speicher - SUN hat sich für Geschwindigkeit entschieden.

Hardcodiert
quelle
1

Boolean stellt eine Information dar, aber seine "Größe" ist nicht genau definiert, sagen Sun Java-Tutorials. Boolesche Literale haben nur zwei mögliche Werte: wahr und falsch. Weitere Informationen finden Sie unter Java-Datentypen .

Krishan
quelle
-10

Warum nicht eine .java-Datei wie folgt erstellen:

Empty.java

class Empty{
}

und eine Klasse wie diese:

NotEmpty.java

class NotEmpty{
   boolean b;
}

Kompilieren Sie beide und vergleichen Sie die .class-Dateien mit einem Hex-Editor.

mring
quelle
5
Dies ist eine weitere Metrik, die nichts mit der Größe des primitiven booleschen Typs im Speicher zu tun hat.
Joel