Sete tipos de coletores de lixo JAVA

Este documento irá explicar o que é o coletor de lixo em java e os principais tipos de coletores de lixo com comportamentos de cada coletor de lixo. Observe também que neste artigo eu não estou explicando como acontece a alocação de pilhas de lixo e como começam os coletores maiores/menores de lixo. Ele irá explicar no próximo artigo.

Java é uma linguagem de programação orientada a objetos que inclui a Coleta Automática de Lixo. Java aloca e desaloca automaticamente a memória para que os programas não sejam sobrecarregados com essa tarefa. Agora (Java-12), Java tem sete tipos de coletores de lixo,

>

  1. Poluidor de Lixo Série
  2. Poluidor de Lixo Paralelo
  3. Poluidor de Lixo CMS
  4. Poluidor de Lixo G1
  5. Poluidor de Lixo Epsilon
  6. Z coletor de lixo
  7. Shenandoah Garbage Collector

5º e 6º coletores de lixo foram introduzidos na java 11 e 7º na java 12. Na maioria das aplicações em produção foram utilizados os primeiros quatro tipos de coletores de lixo. Desde que os últimos três coletores de lixo recentemente entraram em cena.

Precisamos nos preocupar com os GCs?

Sim, de fato. Temos que nos preocupar com o GC e seus comportamentos. Porque Ele pode fornecer uma diferença significativa de desempenho. Cada GC tem as suas próprias vantagens e desvantagens. Como desenvolvedores, temos que ter uma idéia clara sobre os comportamentos de todos os coletores de lixo e temos que selecionar um coletor de lixo com base em nosso cenário de negócios. Podemos escolher um coletor de lixo, passando a escolha como argumento JVM.

Serial Garbage Collector

Esta é a implementação mais simples de GC. Ela foi projetada basicamente para um ambiente de um único fio. Esta implementação de GC congela todas as threads da aplicação quando ela é executada. Ele usa um único fio para a coleta de lixo. Portanto, não é uma boa idéia usá-la em aplicações multi-threaded como ambientes de servidor.

Para habilitar o Serial Garbage Collector, podemos usar o seguinte argumento:

java -XX:+UseSerialGC -jar Application.java

Parallel Garbage Collector

O coletor de lixo paralelo também é chamado de coletor de rendimento. Ao contrário do coletor de lixo em série, este usa múltiplos fios para a coleta de lixo. Similar ao coletor de lixo em série, este também congela todos os fios da aplicação enquanto realiza a coleta de lixo. O coletor de lixo é mais adequado para aquelas aplicações que podem suportar pausas de aplicação.

Para ativar o coletor de lixo paralelo, podemos usar o seguinte argumento:

java -XX:+UseParallelGC -jar Application.java

Se usarmos este GC, podemos especificar o tempo máximo de coleta de lixo e tempo de pausa, rendimento e pegada (tamanho da pilha)

Os números de fios do coletor de lixo podem ser controlados com a opção de linha de comando

-XX:ParallelGCThreads=<N>

O objetivo de tempo máximo de pausa (intervalo entre dois GC)é especificado com a opção de linha de comando

-XX:MaxGCPauseMillis=<N>

O objetivo máximo de rendimento (medido em relação ao tempo gasto na coleta de lixo versus o tempo gasto fora da coleta de lixo) é especificado pela opção de linha de comando

-XX:GCTimeRatio=<N>

Uma área máxima de aterro (a quantidade de memória de aterro que um programa requer durante a execução) é especificada usando a opção -Xmx<N>.

CMS Garbage Collector

Concurrent Mark Sweep (CMS) garbage collector uses multiple garbage collector threads for garbage collector. Ele varre a memória da pilha de lixo para marcar instâncias de despejo e depois varrer as instâncias marcadas. Ele é projetado para aplicativos que preferem pausas mais curtas na coleta de lixo, e que podem se dar ao luxo de compartilhar recursos do processador com o lixeiro enquanto o aplicativo está em execução.

CMS garbage collector retém todos os threads do aplicativo nos dois cenários seguintes apenas

  1. Durante a marcação dos objetos referenciados no espaço da geração antiga.
  2. Ainda mudança na memória da pilha de lixo em paralelo com a coleta de lixo

Em comparação com o coletor de lixo paralelo, o coletor CMS usa mais CPU para garantir um melhor rendimento da aplicação. Se pudermos alocar mais CPU para melhor performance então o CMS coletor de lixo é a escolha preferida em relação ao coletor paralelo.

Para habilitar o CMS Coletor de Lixo, podemos usar o seguinte argumento:

java -XX:+USeParNewGC -jar Application.java

G1 Colector de Lixo

G1 (Garbage First) O Colector de Lixo foi concebido para aplicações em máquinas multi-processadoras com grande espaço de memória. Está disponível desde JDK7 Update 4 e em versões posteriores.

Separa a memória do lixão em regiões e faz a coleta dentro delas em paralelo. G1 também compacta o espaço livre da pilha em movimento logo após a recuperação da memória. Mas o coletor de lixo CMS compacta a memória em situações de parada do mundo (STW). O coletor G1 irá substituir o coletor CMS já que ele é mais eficiente em desempenho.

No coletor G1 contém duas fases;

  1. Marking
  2. Sweeping

Não parecido com outros coletores, o coletor G1 divide a pilha em um conjunto de regiões de pilha de tamanho igual, cada uma delas uma gama contígua de memória virtual. Ao realizar coletas de lixo, G1 mostra uma fase de marcação global simultânea para determinar a vivacidade dos objetos ao longo da pilha.

Após a conclusão da fase de marcação, G1 sabe quais regiões estão mais vazias. Ele recolhe primeiro nestas áreas, o que normalmente rende uma quantidade significativa de espaço livre. É por isso que este método de coleta de lixo é chamado Garbage-First.

Para habilitar o coletor de lixo G1, podemos usar o seguinte argumento:

java -XX:+UseG1GC -jar Application.java

Epsilon Garbage Collector

Epsilon é um coletor de lixo não-operacional ou passivo. Ele aloca a memória para a aplicação, mas não recolhe os objetos não utilizados. Quando a aplicação esgota o Java heap, a JVM desliga. Isto significa que o coletor de lixo Epsilon permite, aplicações ficarem sem memória e travarem.

O objetivo deste coletor de lixo é medir e gerenciar a performance da aplicação. Coletores de lixo ativos são programas complexos que rodam dentro da JVM junto com a sua aplicação. O Epsilon remove o impacto que o GC tem no desempenho. Não há ciclos de GC ou barreiras de leitura ou escrita. Ao usar o GC Epsilon, o código é executado isoladamente. O Epsilon ajuda a visualizar como a coleta de lixo afeta a performance do aplicativo e qual é o limite de memória, uma vez que ele mostrará quando acabar. Como exemplo Se acharmos que só precisamos de um gigabyte de memória para nossa aplicação, podemos executá-la com -Xmx1g e ver o comportamento. Se essa alocação de memória não for suficiente, execute-a novamente com um heap dump. Por favor, note que temos que habilitar esta opção para obter um heap dump. Podemos usar este argumento para obter um heap dump quando a aplicação estiver falhando devido à falta de memória.

XX:HeapDumpOnOutOfMemoryError

Se precisarmos espremer cada pedaço de performance da nossa aplicação, o Epsilon pode ser sua melhor opção para um GC. Mas precisamos ter um entendimento completo de como nosso código usa a memória. Se ele quase não cria lixo ou se você sabe exatamente quanta memória ele usa no período em que roda, o Epsilon é uma opção viável.

Para habilitar o Epsilon Garbage Collector, podemos usar o seguinte argumento:

java -XX:+UseEpsilonGC -jar Application.java

Z garbage collector

ZGC executa todo o trabalho caro ao mesmo tempo, sem parar a execução dos fios da aplicação por mais de 10ms, o que o torna adequado para aplicações que requerem baixa latência e/ou usam uma pilha muito grande. De acordo com a documentação do Oracle, ele pode lidar com pilhas de multi-terabytes. A Oracle introduziu o ZGC no Java 11. O coletor de lixo Z executa seus ciclos em suas roscas. Ele pausa a aplicação por uma média de 1 ms. Os coletores G1 e Parallel têm em média cerca de 200 ms.

Em Java 12, Oracle adiciona correções de performance e descarga de classe mesmo que o Z ainda esteja em estado experimental. Está disponível apenas no Linux de 64 bits. Mas, ZGC tira vantagem dos ponteiros de 64 bits com uma técnica chamada coloração de ponteiro. Os apontadores coloridos armazenam informações extras sobre objetos na pilha. Esta é uma das razões porque está limitada à JVM de 64-bit. Este artigo foi explicado profundamente este cenário (https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/).

ZGC faz a sua marcação em três fases.

1. Breve fase stop-the-world – Examina as raízes GC, variáveis locais que apontam para o resto da pilha. O número total dessas raízes é geralmente mínimo e não se dimensiona com o tamanho da carga, portanto as pausas do ZGC são muito curtas e não aumentam conforme a pilha cresce.

2. Fase concorrente – Ele percorre o gráfico do objeto e examina os ponteiros coloridos, marcando objetos acessíveis. A barreira de carga impede a contenção entre a fase GC e qualquer atividade da aplicação.

3. Fase de realocação – Move objetos vivos para liberar grandes seções do heap para fazer alocações mais rápidas. Quando a fase de realocação começa, o ZGC divide a pilha em páginas e trabalha em uma página de cada vez. Quando o ZGC termina de mover qualquer raiz, o resto da realocação acontece em uma fase concorrente.

ZGC tentará definir o número de threads em si, e normalmente está certo. Mas se o ZGC tiver demasiados threads, ele irá matar a sua aplicação à fome. Se ele não tiver o suficiente, você vai criar lixo mais rápido do que o GC pode coletar. As fases do ZGC ilustram como ele gerencia grandes pilhas de lixo sem afetar a performance à medida que a memória do aplicativo cresce.

Para habilitar o Z Garbage Collector, podemos usar o seguinte argumento:

java -XX:+UseZGC -jar Application.java

Shenandoah

Shenandoah é um coletor de lixo de tempo de pausa ultra-baixo que reduz os tempos de pausa do GC ao executar mais trabalho de coleta de lixo simultaneamente com o programa Java em execução. CMS e G1 executam ambos a marcação simultânea de objetos ao vivo. Shenandoah adiciona compactação concorrente.

Shenandoah usa regiões de memória para gerenciar quais objetos não estão mais em uso e quais estão vivos e prontos para compressão. Shenandoah também adiciona um ponteiro de encaminhamento a cada objeto heap e o usa para controlar o acesso ao objeto. O design do Shenandoah comercializa ciclos de CPU simultâneos e espaço para melhorias no tempo de pausa. O ponteiro de encaminhamento facilita a movimentação de objetos, mas os movimentos agressivos significam que Shenandoah usa mais memória e requer mais trabalho paralelo do que outros GCs. Mas ele faz o trabalho extra com pausas muito breves.

Shenandoah processa a pilha em muitas pequenas fases, a maioria das quais são concomitantes com a aplicação. Este projeto torna possível para o GC gerenciar uma grande pilha de forma eficiente.

  1. Primeira pausa-para-o-mundo no ciclo. Ele prepara o heap para a marcação simultânea e escaneia o conjunto raiz. ComoZGC, a duração desta pausa corresponde ao tamanho do conjunto raiz, não da pilha.
  2. Próximo, uma fase concorrente percorre a pilha e identifica objetos alcançáveis e inalcançáveis.
  3. A terceira termina o processo de marcação drenando as atualizações pendentes da pilha e escaneando novamente o conjunto raiz. Esta fase aciona a segunda parada – a pausa do mundo no ciclo. O número de atualizações pendentes e o tamanho do conjunto raiz determinam quanto tempo a pausa é.
  4. Então, outra fase concorrente copia os objetos fora das regiões identificadas na fase de marcação final. Este processo diferencia Shenandoah de outros GCs já que compacta agressivamente a pilha em paralelo com os threads de aplicação.
  5. A fase seguinte aciona a terceira (e a mais curta) pausa no ciclo. Ela garante que todas as roscas de GC tenham terminado a evacuação.
  6. Quando termina, uma fase concorrente percorre a pilha e atualiza as referências dos objetos movidos anteriormente no ciclo.
  7. A última parada – a pausa do mundo no ciclo termina atualizando as referências atualizando o conjunto raiz. Ao mesmo tempo, ele recicla as regiões evacuadas.
  8. Finalmente, a última fase recupera as regiões evacuadas, que agora não têm referências nelas.

Podemos configurar Shenandoah com uma das três heurísticas. Elas governam quando o CG inicia seus ciclos e como ele seleciona as regiões para evacuação.

1. Adaptativo: Observa os ciclos de GC e inicia o ciclo seguinte para que ele se complete antes que a aplicação esgote a pilha. Esta heurística é o modo padrão.

2. Estático: Inicia um ciclo de GC baseado na ocupação da pilha e na pressão de alocação.

3. Compacto: Executa ciclos de GC continuamente. Shenandoah inicia um novo ciclo assim que os acabamentos anteriores ou com base na quantidade de pilha alocada desde o último ciclo. Esta heurística incorre em custos gerais de produção mas fornece a melhor recuperação de espaço.

Shenandoah precisa coletar a pilha mais rapidamente do que a aplicação que está servindo a aloca. Se a pressão de alocação for muito alta e não houver espaço suficiente para novas alocações, haverá uma falha. Shenandoah tem mecanismos configuráveis para esta situação.

  • Empacotamento: Se Shenandoah começar a ficar atrás da taxa de alocação, ele vai empatar os fios de alocação para alcançar. Os stalls são normalmente suficientes para picos suaves de alocação. Shenandoah introduz atrasos de 10ms ou menos. Se o ritmo falhar, Shenandoah passa para o próximo passo: GC.
  • GC Degenerado: Se ocorrer uma falha na alocação, Shenandoah inicia uma fase stop-the-world. Ele usa a fase para completar o ciclo de GC atual. Uma vez que uma fase stop-the-world não contende com a aplicação para recursos, o ciclo deve terminar rapidamente e limpar o déficit de alocação. Muitas vezes, um ciclo degenerado acontece depois que a maior parte do trabalho do ciclo já está concluída, portanto, a fase stop-the-world é breve. O registro de GC irá relatá-lo como uma pausa completa, embora.
  • GC completo: Se tanto o ritmo como um GC degenerado falhar, Shenandoah cai de volta para um ciclo de GC completo. Este GC final garante que a aplicação não falhará com um erro fora da memória, a menos que não haja mais nenhuma pilha.

Shenandoah oferece as mesmas vantagens que ZGC com pilhas grandes, mas mais opções de ajuste. Dependendo da natureza da sua aplicação, as diferentes heurísticas podem ser um bom ajuste. Seus tempos de pausa podem não ser tão curtos como os do ZGC, mas são mais previsíveis.

Para habilitar o Shenandoah Garbage Collector, podemos usar o seguinte argumento:

java -XX:+UseShenanodoahC -jar Aplicação.java

Conclusão

O objetivo deste artigo é resumir todos os coletores de lixo. Assim, algumas partes do conteúdo foram extraídas das referências dadas. Nós temos que ter uma idéia clara sobre os coletores de lixo para selecionar um coletor de lixo ideal para nossos casos de uso de aplicação. O coletor de lixo ideal melhorará significativamente o desempenho da nossa aplicação.

Referência

  1. https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
  2. https://www.baeldung.com/jvm-garbage-collectors
  3. https://dzone.com/articles/java-garbage-collection-3
  4. https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/
  5. https://openjdk.java.net/projects/shenandoah/
  6. https://docs.oracle.com/en/java/javase/12/gctuning/z-garbage-collector1.html#GUID-A5A42691-095E-47BA-B6DC-FB4E5FAA43D0
  7. http://clojure-goes-fast.com/blog/shenandoah-in-production/

Deixe uma resposta

O seu endereço de email não será publicado.