Compactação de ponteiro Java


O artigo discutirá a implementação da compactação de ponteiro na Java Virtual Machine de 64 bits , que é controlada pela opção UseCompressedOops e é ativada por padrão para sistemas de 64 bits que começam com Java SE 6u23.


Descrição do problema


Em uma JVM de 64 bits, os ponteiros ocupam 2 vezes mais espaço de memória (surpresa-surpresa) do que em um de 32 bits. Isso pode aumentar o tamanho dos dados em 1,5 vezes em comparação com o mesmo código para a arquitetura de 32 bits. Ao mesmo tempo, na arquitetura de 32 bits, apenas 2 ^ 32 bytes (4 GB) podem ser endereçados, o que é bastante pequeno no mundo moderno.


Vamos escrever um pequeno programa e ver quantos bytes os objetos Inteiros ocupam:


import java.util.stream.IntStream; import java.util.stream.Stream; class HeapTest { public static void main(String ... args) throws Exception { Integer[] x = IntStream.range(0, 1_000_000).boxed().toArray(Integer[]::new); Thread.sleep(6000000); Stream.of(x).forEach(System.out::println); } } 

Aqui destacamos um milhão de objetos Inteiros e adormecemos por um longo tempo. A última linha é necessária para que o compilador não ignore repentinamente a criação da matriz (embora, na minha máquina, os objetos sejam criados normalmente sem essa linha).


Compilamos e executamos o programa com compactação de ponteiro desativada:


 > javac HeapTest.java > java -XX:-UseCompressedOops HeapTest 

Usando o utilitário jcmd , examinamos a alocação de memória:


 > jps 45236 HeapTest ... > jcmd 45236 GC.class_histogram 



A imagem mostra que o número total de objetos é 1000128 e o tamanho da memória que esses objetos ocupam é 24003072 bytes . I.e. 24 bytes por objeto (por que exatamente 24 serão escritos abaixo).


E aqui está a memória do mesmo programa, mas com o sinalizador UseCompressedOops ativado :




Agora, cada objeto ocupa 16 bytes .
As vantagens da compactação são óbvias =)


Solução


Como a JVM compacta ponteiros? Essa técnica é chamada de Opa compactada . Oop significa ponteiro de objeto comum ou ponteiro de objeto comum .


O truque é que, em um sistema de 64 bits, os dados na memória estão alinhados com a palavra da máquina, ou seja, 8 bytes cada. E o endereço sempre tem três bits zero no final.


Se você salvar o ponteiro deslocando o endereço em 3 bits para a direita (a operação é chamada de codificação ) e, antes de usar, alternar em 3 bits para a esquerda (respectivamente, decodificar ), poderá ajustar ponteiros de 32 bits com um tamanho de 35 bits , ou seja, Endereço de até 32 GB (2 ^ 35 bytes).


Se o tamanho da pilha do seu programa for superior a 32 GB, a compactação deixará de funcionar e todos os ponteiros terão 8 bytes de tamanho.


Quando a opção UseCompressedOops está ativada, os seguintes tipos de ponteiros são compactados:


  • Campo de classe para cada objeto
  • Objetos de campo de classe
  • Elementos de uma matriz de objetos.

Os objetos da própria JVM nunca são compactados. Nesse caso, a compactação ocorre no nível da máquina virtual, e não no bytecode.


Leia mais sobre como colocar objetos na memória


Agora, vamos usar o utilitário jol (Java Object Layout) para examinar mais de perto a quantidade de memória que nosso Inteiro ocupa em diferentes JVMs:


 > java -jar jol-cli-0.9-full.jar estimates java.lang.Integer ***** 32-bit VM: ********************************************************** java.lang.Integer object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 8 (object header) N/A 8 4 int Integer.value N/A 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ***** 64-bit VM: ********************************************************** java.lang.Integer object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 16 (object header) N/A 16 4 int Integer.value N/A 20 4 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ***** 64-bit VM, compressed references enabled: *************************** java.lang.Integer object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int Integer.value N/A Instance size: 16 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total ***** 64-bit VM, compressed references enabled, 16-byte align: ************ java.lang.Integer object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int Integer.value N/A Instance size: 16 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total 

A diferença entre "VM de 64 bits" e "VM de 64 bits, referências compactadas ativadas" é reduzir o cabeçalho do objeto em 4 bytes. Além disso, no caso sem compactação, torna-se necessário adicionar mais 4 bytes para alinhar os dados na memória.


O que é esse cabeçalho de objeto? Por que diminuiu em 4 bytes?



A imagem mostra um cabeçalho de objeto de 12 bytes, ou seja, com a opção UseCompressedOops ativada. O cabeçalho consiste em alguns sinalizadores da JVM internos, bem como um ponteiro para a classe deste objeto. Pode-se ver que o ponteiro para a classe leva 32 bits. Sem compactação, ele ocuparia 64 bits e o tamanho do cabeçalho do objeto já seria de 16 bytes.


A propósito, você pode ver que há outra opção para o alinhamento de 16 bytes. Nesse caso, você pode aumentar a memória até 64 GB.


Contras Compressão de ponteiros


A compactação de ponteiros, é claro, tem um óbvio menos - o custo das operações de codificação e decodificação cada vez que o ponteiro é acessado. Os números exatos variam de acordo com a aplicação.


Por exemplo, aqui está um gráfico de pausa do coletor de lixo para ponteiros compactados e não compactados, extraídos daqui Java GC em Numbers - OOPs compactados



Pode-se observar que, com a compactação ativada, as pausas do GC duram mais. Você pode ler mais sobre isso no próprio artigo (o artigo é bastante antigo - 2013).


Referências:


Opa compactado na JVM do ponto de acesso
Como a JVM aloca objetos
CompressedOops: Introdução às referências compactadas em Java
Truque por trás de Oops compactados da JVM
Aprimoramentos de desempenho da máquina virtual Java HotSpot

Source: https://habr.com/ru/post/pt440166/


All Articles