Perché la dimensione primitiva booleana di Java non è definita?


111

La specifica Java Virtual Machine afferma che il supporto per i tipi primitivi booleani è limitato .

Non esistono istruzioni per macchine virtuali Java dedicate esclusivamente alle operazioni sui valori booleani. Al contrario, le espressioni nel linguaggio di programmazione Java che operano su valori booleani vengono compilate per utilizzare i valori del tipo di dati int di Java virtual machine.

Quanto sopra implica (anche se potrei averlo interpretato male) che il tipo di dati int viene utilizzato quando si opera su valori booleani, ma questo è un costrutto di memoria a 32 bit. Dato che un booleano rappresenta solo 1 bit di informazione:

  • Perché un tipo byte, o breve, non viene utilizzato come proxy per un booleano invece di int?
  • Qual è il modo più affidabile per scoprire esattamente quanta memoria viene utilizzata per memorizzare un tipo booleano per qualsiasi JVM?

Risposte:


116

Risposta breve: sì, i valori booleani vengono manipolati come entità a 32 bit, ma gli array di booleani utilizzano 1 byte per elemento.

Risposta più lunga: la JVM utilizza una cella stack a 32 bit, utilizzata per contenere variabili locali, argomenti di metodo e valori di espressione. Le primitive più piccole di 1 cella vengono riempite, le primitive più grandi di 32 bit (lunghe e doppie) prendono 2 celle. Questa tecnica riduce al minimo il numero di opcode, ma ha alcuni effetti collaterali peculiari (come la necessità di mascherare i byte).

Le primitive memorizzate negli array possono utilizzare meno di 32 bit e ci sono diversi codici operativi per caricare e memorizzare i valori primitivi da un array. Boolean e byte valori sia uso la baloade bastorecodici operativi, il che implica che gli array booleani prendere 1 byte per elemento.

Per quanto riguarda il layout degli oggetti in memoria, questo è coperto dalle regole di "implementazione privata" , può essere 1 bit, 1 byte o, come notato da un altro poster, allineato a un confine di doppia parola a 64 bit. Molto probabilmente, prende la dimensione della parola di base dell'hardware sottostante (32 o 64 bit).


Per quanto riguarda la riduzione al minimo della quantità di spazio che i booleani utilizzano: non è davvero un problema per la maggior parte delle applicazioni. Gli stack frame (che contengono variabili locali e argomenti del metodo) non sono molto grandi, e nello schema grande un booleano discreto in un oggetto non è nemmeno così grande. Se hai molti oggetti con molti valori booleani, puoi usare i campi di bit che sono gestiti tramite i tuoi getter e setter. Tuttavia, pagherai una penalità nel tempo della CPU che è probabilmente maggiore della penalità in memoria.


Per i membri della classe booleana / byte, è anche vero che sono anche 4 byte? L'istanza della classe viene allocata nel suo insieme sullo stack, quindi posso immaginare che JVM dovrebbe probabilmente utilizzare 1 byte per membro booleano / byte e infine creare un allineamento a 4 byte per l'istanza della classe completa. È così? (se hai riferimenti che lo dimostrano, per favore, condividi)
dma_k

@dma_k: come notato nella mia risposta, il layout di un'istanza di classe dipende dall'implementazione. Tuttavia, nota che le istanze di classe non sono memorizzate nello stack, sono memorizzate nell'heap (sebbene vedrai alcuni riferimenti a JDK 7 "escape analysis" che sposta gli oggetti da uno stack all'altro, questo non sembra essere il caso; vedi java.sun.com/javase/7/docs/technotes/guides/vm/…)
kdgregory

1
A volte impacchettare booleani può essere effettivamente più veloce. Ogni volta che la dimensione della cache è importante, potrebbe essere meglio imballare le cose. Ad esempio, un setaccio primo segmentato funziona in blocchi di 32 kB (dimensione cache L1) è molto più veloce di un setaccio non segmentato. C'è un po 'di overhead tra i pezzi e con l'imballaggio paghi otto volte meno spesso. Non l'ho ancora misurato.
maaartinus

7

Un singolo booleano da qualche parte nella gerarchia di ereditarietà può utilizzare fino a 8 byte! Ciò è dovuto all'imbottitura. Maggiori dettagli possono essere trovati in Quanta memoria viene utilizzata dal mio oggetto Java? :

Tornando alla domanda su quanto consuma un booleano, sì, consuma almeno un byte, ma a causa delle regole di allineamento può consumare molto di più. IMHO è più interessante sapere che un booleano [] consumerà un byte per voce e non un bit, più un po 'di overhead dovuto all'allineamento e al campo delle dimensioni dell'array. Esistono algoritmi di grafi in cui sono utili grandi campi di bit e devi essere consapevole del fatto che, se usi un booleano [], hai bisogno di quasi esattamente 8 volte più memoria di quella realmente necessaria (1 byte contro 1 bit).


In ogni caso, come utilizzerebbe un valore booleano []?
Thomas Jung

booleano [] potrebbe essere utilizzato per una maschera. A volte un BitSet può essere migliore, perché ha alcuni metodi utili.
Michael Munsey

5

La 5a edizione di Java in a Nutshell (O'Reilly) dice che un tipo primitivo booleano è 1 byte. Potrebbe essere sbagliato, in base a ciò che mostra l'esame del mucchio. Mi chiedo se la maggior parte delle JVM abbia problemi con l'allocazione di meno di un byte per le variabili.


3

La mappatura booleana è stata eseguita pensando a una CPU a 32 bit. Il valore int ha 32 bit in modo che possa essere elaborato in un'unica operazione.

Ecco una soluzione dal Java IAQ di Peter Norvig: Domande con risposta di rado per misurare le dimensioni (con qualche imprecisione):

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.");

Poiché questa conversazione riguarda le primitive, dovresti essere creativo nel testarlo poiché le primitive non sono archiviate nell'heap a meno che non siano un campo su un'istanza o un array. E nessuno di questi risponde alla domanda su come Java sceglierà comunque di memorizzarlo nello stack.
Jesse

2

Le CPU operano su una lunghezza del tipo di dati specifica. In caso di CPU a 32 bit, sono lunghe 32 bit e quindi ciò che chiamate "int" in Java. Tutto ciò che è sotto o sopra che deve essere riempito o suddiviso a questa lunghezza prima che la CPU possa elaborarlo. Questo non richiede molto tempo, ma se hai bisogno di 2 cicli di CPU invece di 1 per le operazioni di base, questo significa costi / tempo raddoppiati.

Questa specifica è dedicata alle CPU a 32 bit in modo che possano elaborare booleani con il loro tipo di dati nativo.

Puoi averne solo uno qui: velocità o memoria - SUN ha deciso per la velocità.


1

Boolean rappresenta un po 'di informazione, ma la sua "dimensione" non è qualcosa che è definita con precisione, dicono i tutorial di Sun Java. I valori letterali booleani hanno solo due valori possibili, ovvero vero e falso. Vedere Tipi di dati Java per i dettagli.


-10

Perché non creare un file .java come questo:

Empty.java

class Empty{
}

e una classe come questa:

NotEmpty.java

class NotEmpty{
   boolean b;
}

Compilali entrambi e confronta i file .class con un editor esadecimale.


5
questa è un'altra metrica del tutto, non correlata al dimensionamento del tipo booleano primitivo in memoria.
Joel
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.