Por que o tamanho da primitiva booleana do Java não está definido?

111

A Java Virtual Machine Specification diz que há suporte limitado para tipos primitivos booleanos .

Não há instruções da máquina virtual Java exclusivamente dedicadas a operações em valores booleanos. Em vez disso, as expressões na linguagem de programação Java que operam em valores booleanos são compiladas para usar valores do tipo de dados int da máquina virtual Java.

O acima indica (embora eu possa ter interpretado mal) que o tipo de dados int é usado ao operar em booleanos, mas esta é uma construção de memória de 32 bits. Dado que um booleano representa apenas 1 bit de informação:

  • Por que um byte, ou tipo curto, não é usado como proxy para um booleano em vez de int?
  • Para qualquer JVM, qual é a maneira mais confiável de descobrir exatamente quanta memória é usada para armazenar um tipo booleano?
Joel
fonte

Respostas:

116

Resposta curta: sim, os valores booleanos são manipulados como entidades de 32 bits, mas os arrays de booleanos usam 1 byte por elemento.

Resposta mais longa: a JVM usa uma pilha de células de 32 bits, usada para conter variáveis ​​locais, argumentos de método e valores de expressão. Os primitivos menores que 1 célula são preenchidos, os primitivos maiores que 32 bits (longos e duplos) ocupam 2 células. Essa técnica minimiza o número de opcodes, mas tem alguns efeitos colaterais peculiares (como a necessidade de mascarar bytes).

Primitivos armazenados em matrizes podem usar menos de 32 bits, e existem opcodes diferentes para carregar e armazenar valores primitivos de uma matriz. Os valores booleanos e de byte usam opcodes baloade bastore, o que implica que os arrays booleanos usam 1 byte por elemento.

No que diz respeito ao layout de objeto na memória, isso é coberto pelas regras de "implementação privada" , pode ser de 1 bit, 1 byte ou, como outro usuário observou, alinhado a um limite de palavra dupla de 64 bits. Provavelmente, leva o tamanho de palavra básico do hardware subjacente (32 ou 64 bits).


No que diz respeito a minimizar a quantidade de espaço que os booleanos usam: realmente não é um problema para a maioria dos aplicativos. Os frames da pilha (contendo variáveis ​​locais e argumentos de método) não são muito grandes e, no grande esquema, um booleano discreto em um objeto também não é tão grande. Se você tiver muitos objetos com muitos booleanos, poderá usar campos de bits que são gerenciados por meio de seus getters e setters. No entanto, você pagará uma penalidade no tempo de CPU que provavelmente é maior do que a penalidade na memória.

kdgregory
fonte
Para membros da classe booleana / byte, também é verdade que eles também têm 4 bytes? A instância da classe é alocada como um todo na pilha, então posso imaginar, a JVM provavelmente deve usar 1 byte por membro booleano / byte e, finalmente, fazer um alinhamento de 4 bytes para a instância completa da classe. É assim? (se você tiver referências que comprovem isso, por favor, compartilhe)
dma_k
@dma_k: conforme observado em minha resposta, o layout de uma instância de classe depende da implementação. No entanto, observe que as instâncias de classe não são armazenadas na pilha, mas no heap (embora você veja algumas referências à "análise de escape" do JDK 7 movendo objetos de pilha para heap, esse não parece ser o caso; consulte java.sun.com/javase/7/docs/technotes/guides/vm/…)
kdgregory
1
Às vezes, empacotar booleanos pode ser realmente mais rápido. Sempre que o tamanho do cache for importante, pode ser melhor embalar as coisas. Por exemplo, uma peneira principal segmentada funciona em blocos de 32 kB (tamanho do cache L1) é muito mais rápida do que uma peneira não segmentada. Há alguma sobrecarga entre os pedaços e, com a embalagem, você paga a sobrecarga oito vezes menos. Eu não medi ainda.
maaartinus
7

Um único booleano em algum lugar na hierarquia de herança pode usar até 8 bytes! Isso se deve ao preenchimento. Mais detalhes podem ser encontrados em Quanta memória é usada por meu objeto Java? :

Voltando à questão de quanto um booleano consome, sim, ele consome pelo menos um byte, mas devido às regras de alinhamento, pode consumir muito mais. IMHO é mais interessante saber que um booleano [] consumirá um byte por entrada e não um bit, mais alguma sobrecarga devido ao alinhamento e para o campo de tamanho da matriz. Existem algoritmos de gráfico em que grandes campos de bits são úteis, e você precisa estar ciente de que, se usar um booleano [], você precisará de quase exatamente 8 vezes mais memória do que realmente necessário (1 byte versus 1 bit).

Akuhn
fonte
Como usaria um booleano [] de qualquer maneira?
Thomas Jung
boolean [] pode ser usado para uma máscara. Às vezes, um BitSet pode ser melhor, pois tem alguns métodos úteis.
Michael Munsey
5

A 5ª edição do Java in a Nutshell (O'Reilly) diz que um tipo primitivo booleano tem 1 byte. Isso pode estar errado, com base no que o exame da pilha está mostrando. Eu me pergunto se a maioria das JVMs tem problemas com a alocação de menos de um byte para variáveis.

Matthew Flynn
fonte
3

O mapeamento booleano foi feito com uma CPU de 32 bits em mente. O valor int tem 32 bits, portanto, pode ser processado em uma operação.

Aqui está uma solução do Java IAQ de Peter Norvig: Perguntas pouco respondidas para medir o tamanho (com alguma imprecisão):

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
fonte
Visto que esta conversa é sobre primitivos, você teria que ser criativo ao testar isso, já que os primitivos não são armazenados no heap, a menos que sejam um campo em uma instância ou um array. E nenhum deles responde à pergunta de como o Java escolherá armazená-lo na pilha de qualquer maneira.
Jesse
2

CPUs operam em um comprimento de tipo de dados específico. No caso de CPUs de 32 bits, eles têm 32 bits e, portanto, são chamados de 'int' em Java. Tudo abaixo ou acima disso deve ser preenchido ou dividido neste comprimento antes que a CPU possa processá-lo. Isso não leva muito tempo, mas se você precisar de 2 ciclos de CPU em vez de 1 para operações básicas, isso significa custos / tempo dobrados.

Esta especificação é dedicada a CPUs de 32 bits para que possam processar booleanos com seu tipo de dados nativo.

Você só pode ter um aqui: velocidade ou memória - SUN decidiu pela velocidade.

Codificado
fonte
1

Boolean representa um bit de informação, mas seu "tamanho" não é algo definido com precisão, dizem os tutoriais Sun Java. Literais booleanos têm apenas dois valores possíveis: verdadeiro e falso. Consulte Tipos de dados Java para obter detalhes.

Krishan
fonte
-10

Por que não criar um arquivo .java como este:

Empty.java

class Empty{
}

e uma classe como esta:

NotEmpty.java

class NotEmpty{
   boolean b;
}

Compile os dois e compare os arquivos .class com um editor hexadecimal.

mring
fonte
5
esta é uma outra métrica, não relacionada ao dimensionamento do tipo booleano primitivo na memória.
Joel