OOPs compactados na JVM

OOPs compactados na JVM

1. Visão geral

A JVM gerencia a memória para nós. Isso remove a carga de gerenciamento de memória dos desenvolvedores, ou seja,we don’t need to manipulate object pointers manually, que é comprovadamente demorado e sujeito a erros.

Sob o capô, a JVM incorpora muitos truques bacanas para otimizar o processo de gerenciamento de memória. One trick is the use of*Compressed Pointers*, que avaliaremos neste artigo. Primeiro, vamos ver como a JVM representa objetos em tempo de execução.

2. Representação de objeto em tempo de execução

O HotSpot JVM usa uma estrutura de dados chamadaoops ouOrdinary Object Pointers para representar objetos. Essesoops são equivalentes a ponteiros C nativos. The instanceOops are a special kind of oop that represents the object instances in Java. Além disso, a JVM também oferece suporte a vários outrosoops mantidos emOpenJDK source tree.

Vamos ver como a JVM apresentainstanceOops de memória sin.

2.1. Layout da memória do objeto

O layout de memória de uminstanceOop é simples: é apenas o cabeçalho do objeto imediatamente seguido por zero ou mais referências a campos de instância.

A representação da JVM de um cabeçalho de objeto consiste em:

  • One mark word que serve a muitos propósitos, comoBiased Locking,Identity Hash Values, eGC. Não é umoop,, mas por razões históricas, reside na árvore de origemOpenJDK’s oop.

  • One Klass word which representa um ponteiro para os metadados da classe. Antes do Java 7, eles eram mantidos noPermanent Generation, mas do Java 8 em diante, eles estão noMetaspace.

  • A 32-bit lengthword an campo apenas de matriz para representar o comprimento da matriz.

  • A 32-bit gap para impor o alinhamento do objeto. Isso facilita as coisas, como veremos mais adiante.

Immediately after the header, there are be zero or more references to instance fields. Nesse caso, aword é uma palavra de máquina nativa, portanto, 32 bits em máquinas de 32 bits legadas e 64 bits em sistemas mais modernos.

2.2. Anatomia de Resíduos

Suponha que vamos mudar de uma arquitetura de 32 bits legada para uma máquina de 64 bits mais moderna. No início, podemos esperar um aumento imediato no desempenho. No entanto, nem sempre é o caso quando a JVM está envolvida.

The main culprit for this possible performance degradation is 64-bit object references. referências de 64 bits ocupam o dobro do espaço das referências de 32 bits, portanto, isso leva a mais consumo de memória em geral e a ciclos de GC mais frequentes. Quanto mais tempo dedicado aos ciclos do GC, menos fatias de execução da CPU para nossos encadeamentos de aplicativos.

Então, devemos voltar e usar essas arquiteturas de 32 bits novamente? Mesmo se isso fosse uma opção, não poderíamos ter mais de 4 GB de espaço de heap em espaços de processo de 32 bits sem um pouco mais de trabalho.

3. OOPs compactados

Acontece que a JVM pode evitar o desperdício de memória compactando os ponteiros de objeto ouoops,  para que possamos ter o melhor dos dois mundos:allowing more than 4 GB of heap space with 32-bit references in 64-bit machines!

3.1.  Otimização básica

Como vimos anteriormente, a JVM adiciona preenchimento aos objetos para que seu tamanho seja múltiplo de 8 bytes. With these paddings, the last three bits in oops are always zero. Isso ocorre porque os números que são múltiplos de 8 sempre terminam em000 em binário.

image

Uma vez que a JVM já sabe que os últimos três bits são sempre zero, não há nenhum ponto em armazenar esses zeros insignificantes no heap. Em vez disso, ele assume que eles estão lá e armazena 3 outros bits mais significativos que não cabíamos em 32 bits anteriormente. Agora, temos um endereço de 32 bits com 3 zeros deslocados para a direita, então estamos compactando um ponteiro de 35 bits em um de 32 bits. This means that we can use up to 32 GB –  232+3=235=32 GB – of heap space without using 64-bit references.

Para fazer essa otimização funcionar, quando a JVM precisa encontrar um objeto na memóriait shifts the pointer to left by 3 bits (basicamente adiciona esses 3 zeros de volta ao final). Por outro lado, ao carregar um ponteiro na pilha, a JVM move o ponteiro para a direita em 3 bits para descartar os zeros adicionados anteriormente. Basically, the JVM performs a little bit more computation to save some space.  Felizmente, o deslocamento de bits é uma operação realmente trivial para a maioria das CPUs.

Para habilitar a compressãooop , podemos usar o sinalizador de atordoamento-XX:+UseCompressedOops . A compressãooop é o comportamento padrão do Java 7 em diante, sempre que o tamanho máximo de heap for menor que 32 GB. When the maximum heap size is more than 32 GB, the JVM will automatically switch off the oop compression. Portanto, a utilização de memória além de um tamanho de heap de 32 Gb precisa ser gerenciada de maneira diferente.

3.2. Beyond 32 GB

Também é possível usar ponteiros compactados quando os tamanhos de heap Java são maiores que 32 GB. Although the default object alignment is 8 bytes, this value is configurable using the -XX:ObjectAlignmentInBytes tuning flag. The specified value should be a power of two and must be within the range of 8 and 256.

Podemos calcular o tamanho máximo possível de heap com ponteiros compactados da seguinte maneira:

4 GB * ObjectAlignmentInBytes

Por exemplo, quando o alinhamento do objeto é de 16 bytes, podemos usar até 64 GB de espaço de heap com ponteiros compactados.

Observe que, à medida que o valor do alinhamento aumenta, o espaço não utilizado entre os objetos também aumenta. Como resultado, podemos não obter benefícios ao usar ponteiros compactados com grandes tamanhos de heap Java.

3.3.  GCs futurísticos

ZGC, uma nova adição emJava 11, é um coletor de lixo de baixa latência experimental e escalável. Ele pode lidar com diferentes faixas de tamanhos de heap, mantendo as pausas do GC abaixo de 10 milissegundos. Como o ZGC precisa usar64-bit colored pointers,it does not support compressed references. Portanto, o uso de um GC de latência ultra baixa como o ZGC deve ser trocado novamente usando mais memória.

4. Conclusão

Neste artigo, descrevemos aJVM memory management issue in 64-bit architectures. Vimoscompressed pointers and object alignment e vimos como a JVM pode resolver esses problemas, permitindo-nos tamanhos de heap maiores com menos indicadores de desperdício e um mínimo de computação extra.