¿Por qué no está definido el tamaño primitivo booleano de Java?

111

La especificación de la máquina virtual de Java dice que hay un soporte limitado para los tipos primitivos booleanos .

No hay instrucciones de máquina virtual Java dedicadas exclusivamente a operaciones con valores booleanos. En su lugar, las expresiones en el lenguaje de programación Java que operan con valores booleanos se compilan para utilizar valores del tipo de datos int de la máquina virtual Java.

Lo anterior implica (aunque puedo haberlo malinterpretado) que el tipo de datos int se usa cuando se opera con valores booleanos, pero esta es una construcción de memoria de 32 bits. Dado que un booleano solo representa 1 bit de información:

  • ¿Por qué no se usa un byte, o un tipo corto, como proxy para un booleano en lugar de int?
  • Para cualquier JVM dada, ¿cuál es la forma más confiable de averiguar exactamente cuánta memoria se usa para almacenar un tipo booleano?
Joel
fuente

Respuestas:

116

Respuesta corta: sí, los valores booleanos se manipulan como entidades de 32 bits, pero las matrices de booleanos usan 1 byte por elemento.

Respuesta más larga: la JVM usa una celda de pila de 32 bits, que se usa para contener variables locales, argumentos de método y valores de expresión. Las primitivas de menos de 1 celda se rellenan, las primitivas de más de 32 bits (largas y dobles) toman 2 celdas. Esta técnica minimiza el número de códigos de operación, pero tiene algunos efectos secundarios peculiares (como la necesidad de enmascarar bytes).

Las primitivas almacenadas en matrices pueden usar menos de 32 bits y existen diferentes códigos de operación para cargar y almacenar valores primitivos de una matriz. Los valores booleanos y de bytes usan ambos códigos de operación baloady bastore, lo que implica que las matrices booleanas toman 1 byte por elemento.

En lo que respecta al diseño de objetos en memoria, esto está cubierto por las reglas de "implementación privada" , puede ser de 1 bit, 1 byte o, como señaló otro cartel, alineado con un límite de palabra doble de 64 bits. Lo más probable es que tome el tamaño de palabra básico del hardware subyacente (32 o 64 bits).


En cuanto a minimizar la cantidad de espacio que usan los booleanos: realmente no es un problema para la mayoría de las aplicaciones. Los marcos de pila (que contienen variables locales y argumentos de método) no son muy grandes y, en el esquema grande, un booleano discreto en un objeto tampoco es tan grande. Si tiene muchos objetos con muchos valores booleanos, puede usar campos de bits que se administran a través de sus getters y setters. Sin embargo, pagará una penalización en el tiempo de CPU que probablemente sea mayor que la penalización en la memoria.

kdgregory
fuente
Para los miembros de la clase booleana / byte, ¿también es cierto que también tienen 4 bytes? La instancia de clase se asigna como un todo en la pila, por lo que puedo imaginar que JVM probablemente debería usar 1 byte por miembro booleano / byte y finalmente hacer una alineación de 4 bytes para la instancia de clase completa. ¿Es tan? (si tiene referencias que prueben esto, por favor, comparta)
dma_k
@dma_k: como se señaló en mi respuesta, el diseño de una instancia de clase depende de la implementación. Sin embargo, tenga en cuenta que las instancias de clase no se almacenan en la pila, se almacenan en el montón (aunque verá algunas referencias al "análisis de escape" del JDK 7 moviendo objetos de una pila a otra, este no parece ser el caso; ver java.sun.com/javase/7/docs/technotes/guides/vm/…)
kdgregory
1
A veces, empaquetar valores booleanos puede ser más rápido. Siempre que el tamaño de la caché sea importante, puede ser mejor empaquetar las cosas. Por ejemplo, un tamiz primario segmentado funciona en trozos de 32 kB (tamaño de caché L1) es mucho más rápido que un tamiz no segmentado. Hay algunos gastos generales entre los trozos y con el embalaje, los gastos generales se pagan ocho veces menos. Aún no lo he medido.
maaartinus
7

¡Un solo booleano en algún lugar de la jerarquía de herencia puede usar hasta 8 bytes! Esto se debe al acolchado. Se pueden encontrar más detalles en ¿Cuánta memoria utiliza mi objeto Java? :

Volviendo a la cuestión de cuánto consume un booleano, sí, consume al menos un byte, pero debido a las reglas de alineación puede consumir mucho más. En mi humilde opinión, es más interesante saber que un booleano [] consumirá un byte por entrada y no un bit, más algo de sobrecarga debido a la alineación y al campo de tamaño de la matriz. Hay algoritmos de gráficos en los que son útiles grandes campos de bits, y debe tener en cuenta que, si usa un booleano [], necesita casi exactamente 8 veces más memoria de la que realmente necesita (1 byte frente a 1 bit).

akuhn
fuente
¿Cómo usaría un booleano [] de todos modos?
Thomas Jung
boolean [] podría usarse para una máscara. Sin embargo, a veces, un BitSet puede ser mejor, porque tiene algunos métodos útiles.
Michael Munsey
5

La quinta edición de Java in a Nutshell (O'Reilly) dice que un tipo primitivo booleano es de 1 byte. Eso podría estar mal, según lo que muestra el examen del montón. Me pregunto si la mayoría de las JVM tienen problemas para asignar menos de un byte para las variables.

Matthew Flynn
fuente
3

El mapeo booleano se realizó con una CPU de 32 bits en mente. El valor int tiene 32 bits, por lo que se puede procesar en una sola operación.

Aquí hay una solución de Java IAQ de Peter Norvig: Preguntas con respuestas poco frecuentes para medir el tamaño (con cierta imprecisión):

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
fuente
Dado que esta conversación trata sobre primitivas, tendría que ser creativo al probar esto, ya que las primitivas no se almacenan en el montón a menos que sean un campo en una instancia o una matriz. Y ninguno de ellos responde a la pregunta de cómo elegirá Java almacenarlo en la pila de todos modos.
Jesse
2

Las CPU operan en una longitud de tipo de datos específica. En el caso de las CPU de 32 bits, tienen una longitud de 32 bits y, por lo tanto, lo que se llama 'int' en Java. Todo lo que esté por debajo o por encima de lo que debe llenarse o dividirse a esta longitud antes de que la CPU pueda procesarlo. Esto no lleva mucho tiempo, pero si necesita 2 ciclos de CPU en lugar de 1 para operaciones básicas, esto significa duplicar los costos / tiempo.

Esta especificación está dedicada a las CPU de 32 bits para que puedan procesar valores booleanos con su tipo de datos nativo.

Aquí solo puede tener uno: velocidad o memoria: SUN se decidió por la velocidad.

Codificado
fuente
1

Boolean representa un bit de información, pero su "tamaño" no es algo que esté definido con precisión, dicen los tutoriales de Sun Java. Los literales booleanos tienen solo dos valores posibles: verdadero y falso. Consulte Tipos de datos de Java para obtener más detalles.

Krishan
fuente
-10

¿Por qué no crear un archivo .java como este?

Empty.java

class Empty{
}

y una clase como esta:

NotEmpty.java

class NotEmpty{
   boolean b;
}

Compile ambos y compare los archivos .class con un editor hexadecimal.

mring
fuente
5
esta es otra métrica en total, no relacionada con el tamaño del tipo booleano primitivo en la memoria.
Joel