JavaRush /Blogue Java /Random-PT /Pausa para café #210. Todos os tipos de coletores de lixo...

Pausa para café #210. Todos os tipos de coletores de lixo em Java que você deve conhecer

Publicado no grupo Random-PT
Fonte: Hackernoon Através deste post, você conhecerá os pontos fortes e fracos de cada tipo de coletor de lixo utilizado no desenvolvimento Java. Pausa para café #210.  Todos os tipos de coletores de lixo em Java que você deve conhecer - 1A pergunta sobre o Coletor de Lixo (GC) pode ser ouvida em quase todas as entrevistas. Então decidi coletar todas as informações necessárias sobre eles usando meu princípio favorito - curto e simples. Primeiro, vamos começar com o propósito do CG e por que precisamos de vários tipos de coletores de lixo. Em linguagens como C, precisamos armazenar informações de objetos na memória e escrever muito código padrão para liberar essa memória. É claro que vazamentos de memória são comuns nesses programas. Java resolve o problema de vazamento de memória usando um coletor de lixo. E você, como desenvolvedor, deve saber qual coletor de lixo é melhor usar. Depende muito de onde e como seu programa é executado. Ele pode rodar em hardware fraco ou com um grande número de objetos, ou seu programa deve ser muito rápido. Com base nessas condições, você deve ajustar seu coletor de lixo para atingir o desempenho desejado. Então, vamos começar.

Como a JVM lida com a memória

A Java Virtual Machine (JVM) divide a memória em duas áreas: o heap, que armazena dados do aplicativo, e o não-heap, que armazena o código do programa e outros dados. Vamos voltar nossa atenção para a área do heap. É aqui que nosso programa cria novos objetos. Todos os coletores de lixo são baseados no fato de que muitos programas usam objetos efêmeros. Ou seja, esses objetos foram criados, depois cumpriram sua função e não são mais necessários. A maioria desses objetos. Mas alguns objetos duram muito mais tempo, talvez até durante toda a duração do programa. É aí que surge a ideia de dividir os objetos em gerações jovens e velhas. E precisamos verificar a geração mais jovem com muita frequência. O fato é que os processos de coleta de lixo se dividem em limpeza menor, que atinge apenas a geração mais jovem, e limpeza completa, que pode afetar ambas as gerações. Lembre-se de que o coletor de lixo é um programa. E requer tempo e recursos do seu computador para funcionar. O que também afeta nossa aplicação. Como isso afeta? Por exemplo, para realizar a coleta de lixo, a JVM pausa nosso aplicativo. Isso é chamado de pausa Stop-The-World (STW). Durante esse período, todos os threads do aplicativo serão suspensos. Mas o aplicativo interno desconhece completamente isso. Para a aplicação, o tempo flui uniformemente. Por que isso é tão ruim? Imagine que você está escrevendo algum tipo de aplicativo de troca ou de piloto automático de avião. Seu aplicativo pode entrar em suspensão por um segundo e a natureza do seu problema pode mudar drasticamente. Ou seja, a pausa é um parâmetro significativo para todo coletor de lixo. A próxima propriedade fundamental do coletor de lixo é o tempo total gasto na coleta de lixo em relação ao tempo total de execução do programa. O que isso significa e por que é tão importante? Em vez de uma grande fase “Stop-The-World”, podemos escolher um algoritmo com muitas pequenas pausas. Pequenas pausas são preferíveis, mas nada vem de graça. Neste caso, pagamos aumentando o tempo total de execução do programa. E devemos levar isso em conta também. O próximo parâmetro é a quantidade de recursos de hardware. Cada coletor precisa de memória para armazenar informações do objeto e de um processador para realizar a limpeza. O último parâmetro é a velocidade. A eficiência da coleta de lixo refere-se à rapidez e eficiência com que o coletor de lixo (GC) recupera a memória que não é mais usada por um programa. Todos esses parâmetros influenciam o algoritmo, que pode liberar memória o mais rápido possível e consumir recursos mínimos. Vamos dar uma olhada nos coletores de lixo disponíveis para nós. Para a entrevista você precisa conhecer os cinco primeiros. Os outros dois são muito mais difíceis.

GC serial

Serial GC é o coletor de lixo da Java Virtual Machine e tem sido usado desde o início do Java. É útil para programas com um heap pequeno e executados em máquinas menos potentes. Este coletor de lixo divide o heap em regiões, que incluem Eden e Survivor. A região Eden é o pool a partir do qual a memória para a maioria dos objetos é inicialmente alocada. Survivor é um pool contendo objetos que sobreviveram à coleta de lixo na região do Éden. À medida que a pilha é preenchida, os objetos são movidos entre as regiões Eden e Survivor. A JVM monitora constantemente o movimento de objetos nas regiões Survivor e seleciona um limite apropriado para o número de tais movimentos, após o qual os objetos são movidos para a região Tenured. Quando não há espaço suficiente na região Titulada, a coleta de lixo integral assume, atuando em objetos de ambas as gerações. A principal vantagem deste coletor de lixo é a baixa necessidade de recursos, portanto, um processador de baixo consumo de energia é suficiente para realizar a coleta. A principal desvantagem do Serial GC são as longas pausas durante a coleta de lixo, principalmente quando se trata de grandes quantidades de dados.

CG paralelo

Um coletor de lixo paralelo (Parallel CG) é semelhante a um construtor sequencial. Inclui processamento paralelo de algumas tarefas e a capacidade de ajustar automaticamente as configurações de desempenho. Parallel GC é um coletor de lixo Java Virtual Machine baseado nas ideias do Serial GC, mas com paralelismo e inteligência adicionais. Se o computador tiver mais de um núcleo de processador, a versão mais antiga da JVM selecionará automaticamente Parallel GC. O heap aqui é dividido nas mesmas regiões do Serial GC - Eden, Survivor 0, Survivor 1 e Old Gen (Tenured). No entanto, vários threads participam da coleta de lixo em paralelo e o coletor pode se ajustar aos parâmetros de desempenho necessários. Cada thread coletor possui uma área de memória que precisa ser limpa. O Parallel GC também possui configurações que visam atingir a eficiência necessária na coleta de lixo. O coletor usa estatísticas de coletas de lixo anteriores para ajustar as configurações de desempenho para coletas futuras. O Parallel GC fornece ajuste automático de parâmetros de desempenho e tempos de pausa de construção mais baixos, mas há uma pequena desvantagem na forma de alguma fragmentação de memória. É adequado para a maioria dos aplicativos, mas para programas mais complexos é melhor escolher implementações de coletor de lixo mais avançadas. Prós: Mais rápido que o Serial GC em muitos casos. Tem boa velocidade. Contras: Consome mais recursos e as pausas podem ser bastante longas, mas podemos ajustar a duração máxima da pausa do Stop-The-World.

Varredura de marca simultânea

O coletor de lixo Concurrent Mark Sweep (CMS) visa reduzir a duração máxima da pausa executando algumas tarefas de coleta de lixo simultaneamente com threads do aplicativo. Este coletor de lixo é adequado para gerenciar grandes quantidades de dados na memória. Concurrent Mark Sweep (CMS) é uma alternativa ao Parallel GC na Java Virtual Machine (JVM). Destina-se a aplicações que requerem acesso a múltiplos núcleos de processador e são sensíveis a pausas Stop-The-World. O CMS executa etapas de coleta de lixo em paralelo com o programa principal, o que permite que ele seja executado sem parar. Ele usa a mesma organização de memória que os coletores Serial e Paralelo, mas não espera que a área Tenured seja preenchida antes de executar a limpeza da geração antiga. Em vez disso, ele é executado em segundo plano e tenta manter a região controlada compacta. O Concurrent Mark Sweep começa com uma fase inicial de marcação que interrompe brevemente os threads principais do aplicativo e marca todos os objetos acessíveis a partir do root. Os threads principais do aplicativo são então retomados e o CMS começa a procurar todos os objetos ativos que são acessíveis por links dos objetos raiz marcados. Depois de marcar todos os objetos vivos, o coletor limpa a memória dos objetos mortos em vários threads paralelos. Um dos benefícios de um CMS é o foco na minimização do tempo de inatividade, o que é fundamental para muitas aplicações. No entanto, requer sacrifício em termos de recursos de CPU e largura de banda geral. Além disso, o CMS não compacta objetos da geração antiga, o que leva à fragmentação. Pausas longas devido a possíveis falhas no modo paralelo podem ser uma surpresa desagradável (embora não aconteçam com frequência). Se houver memória suficiente, o CMS poderá evitar essas pausas. Prós: Rápido. Tem pequenas pausas Stop-The-World. Contras: consome mais memória; se não houver memória suficiente, algumas pausas podem ser longas. Não é muito bom se o aplicativo criar muitos objetos.

Lixo primeiro

Garbage-First (G1) é considerado uma alternativa a um CMS, especialmente para aplicativos de servidor executados em servidores multiprocessadores e gerenciando grandes conjuntos de dados. O coletor de lixo G1 converte a memória em diversas regiões de tamanhos iguais, com exceção de regiões enormes (que são criadas pela fusão de regiões regulares para acomodar objetos massivos). As regiões não precisam ser organizadas consecutivamente e podem mudar sua afiliação geracional. Pequenas expurgos são realizadas periodicamente para a geração mais jovem e movem objetos para regiões Sobreviventes ou atualizam-nos para a geração mais velha e transferem-nos para Titulares. A limpeza é realizada apenas nas regiões onde é necessário evitar ultrapassar o tempo desejado. O próprio catador prevê e seleciona as regiões com maior quantidade de lixo para limpeza. As varreduras completas usam um loop de marcação para criar uma lista de objetos ativos que são executados em paralelo com o aplicativo principal. Após o ciclo de marcação, G1 passa a executar purgas mistas, que adicionam regiões de geração mais antiga ao conjunto de regiões de geração mais jovem a serem purgadas. O coletor de lixo G1 é considerado mais preciso que o coletor CMS na previsão de tamanhos de pausa e distribui melhor a coleta de lixo ao longo do tempo para evitar longos tempos de inatividade do aplicativo, especialmente com tamanhos de heap grandes. Também não fragmenta a memória como o coletor CMS. No entanto, o coletor G1 requer mais recursos de CPU para ser executado em paralelo com o programa principal, o que reduz o rendimento do aplicativo. Prós: Funciona melhor que CMS. Tem pausas mais curtas. Contras: Consome mais recursos da CPU. Também consome mais memória se tivermos muitos objetos grandes (mais de 500 KB), porque coloca esses objetos em uma região (1-32 MB).

Épsilon GC

O Epsilon GC foi projetado para situações onde a coleta de lixo não é necessária. Ele não executa a coleta de lixo, mas usa TLAB (buffers de alocação local de thread) para alocar novos objetos - pequenos buffers de memória solicitados por threads individuais do heap. Objetos enormes que não cabem no buffer solicitam blocos de memória especificamente para si. Quando o Epsilon GC fica sem recursos, um OutOfMemoryError é gerado e o processo é encerrado. Os benefícios do Epsilon GC incluem menores requisitos de recursos e alocação de memória mais rápida para aplicativos que criam todos os objetos necessários na inicialização ou executam aplicativos de curta duração que não usam toda a memória alocada. O Epsilon GC também pode ajudar a analisar os requisitos de recursos que outros coletores de lixo adicionam ao seu aplicativo. Prós: Muito rápido. Contras: Não limpa objetos :) Os próximos dois coletores são os mais avançados do gênero, mas também os mais complexos. Portanto, iremos considerá-los brevemente.

ZGC

O ZGC pode manter latência inferior a um milissegundo mesmo ao lidar com grandes quantidades de dados. ZGC é um coletor de lixo desenvolvido pela Oracle para Java projetado para fornecer alto rendimento e baixa latência ao processar grandes heaps (até 16 TB). ZGC é baseado em princípios de memória virtual e usa marcações de cores diferentes para rastrear o estado dos objetos durante a coleta de lixo. Prós: As pausas duram menos de um milissegundo, mesmo em heaps grandes, o que é muito útil para aplicativos que exigem tempos curtos de processamento de consultas. Funciona com heaps muito grandes com bom rendimento. O ZGC pode compactar a memória heap durante a coleta de lixo. Contras: Alto uso de CPU e requisitos significativos de desempenho, que podem retardar o tempo de inicialização de aplicativos.

Shenandoah G.C.

Shenandoah GC é outro coletor de lixo com pausas curtas, independentemente do tamanho do heap. Este coletor de lixo foi desenvolvido pela Red Hat. Ele foi projetado para minimizar o tempo que um aplicativo gasta na coleta de lixo. Assim como o ZGC, é um coletor paralelo, o que significa que funciona enquanto a aplicação está em execução, minimizando pausas. Shenandoah GC usa “ponteiros de encaminhamento” para mover objetos durante a coleta de lixo. Também possui uma técnica chamada “remoção de barreira de carga” para melhorar o desempenho. Prós: O Shenandoah GC pode atingir tempos de pausa curtos, geralmente inferiores a 10 ms, mesmo para pilhas enormes. Bom rendimento. Contras: alta carga do processador e dificuldade de trabalhar sob cargas pesadas.

Conclusão

Os coletores de lixo são uma das tarefas mais difíceis da programação. Novos desenvolvimentos estão constantemente sendo realizados nessa direção. Embora seja raro os programadores ajustarem o GC, você ainda precisa ter pelo menos um conhecimento superficial de como funciona sua ferramenta de coleta de lixo.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION