CodeGym /Cursos /JAVA 25 SELF /Coletores de lixo: G1, ZGC, Shenandoah, comparação

Coletores de lixo: G1, ZGC, Shenandoah, comparação

JAVA 25 SELF
Nível 64 , Lição 1
Disponível

1. Introdução ao coletor de lixo (GC)

Se você já programou em C ou C++, certamente teve de liberar memória manualmente com free() ou delete. Em Java, tudo é bem mais simples: você cria um objeto com new e não precisa removê-lo — isso é tarefa de um “zelador” especial chamado coletor de lixo (Garbage Collector, GC).

O GC é parte da JVM que libera automaticamente a memória ocupada por objetos que não possuem mais referências. Graças a ele, desenvolvedores Java não precisam se preocupar em esquecer de limpar a memória (e ter vazamento) ou, ao contrário, remover acidentalmente um objeto ainda necessário (e causar um crash).

Mas, como qualquer zelador, o GC não é perfeito: às vezes pode interferir no pior momento, fazendo uma “faxina geral” (Stop-the-World), ou não trabalhar tão rápido quanto gostaríamos. Por isso, a JVM oferece várias implementações de coletores de lixo — e escolher a correta pode impactar significativamente o desempenho do aplicativo.

Tipos principais de coletores de lixo

Serial GC

  • Serial GC — o coletor de lixo mais simples e antigo.
  • Trabalha em uma única thread.
  • Interrompe todas as outras threads durante a coleta (Stop-the-World).
  • Bom para aplicativos pequenos sem multithreading intenso.
  • Ativado com o flag: -XX:+UseSerialGC

Parallel GC

  • Parallel GC (também “Throughput Collector”).
  • Usa múltiplas threads para a coleta de lixo.
  • Focado em máxima vazão (throughput).
  • Ainda realiza a coleta com pausas Stop-the-World, mas mais rápido que o Serial.
  • Adequado para aplicativos de servidor em que pequenas pausas não são críticas.
  • Ativado com o flag: -XX:+UseParallelGC

CMS (Concurrent Mark Sweep)

  • CMS — GC obsoleto, mas por muito tempo popular, que minimiza pausas.
  • Trabalha parcialmente em paralelo com o aplicativo, reduzindo o tempo de parada.
  • Mais complexo de configurar e com sobrecarga adicional.
  • Desde o Java 9 está marcado como obsoleto (deprecated).
  • Ativado com o flag: -XX:+UseConcMarkSweepGC

G1 (Garbage First)

  • G1 GC — coletor moderno padrão (a partir do Java 9).
  • Equilibra pausas mínimas e desempenho.
  • Divide o heap em muitas regiões (modelo por regiões).
  • Pode coletar lixo seletivamente por regiões, sem tocar o heap inteiro.
  • Permite definir uma pausa máxima alvo, por exemplo -XX:MaxGCPauseMillis=200.
  • Flag de ativação: -XX:+UseG1GC (geralmente desnecessário, pois o G1 é o padrão).

ZGC e Shenandoah

  • ZGC e Shenandoah — coletores de lixo modernos de baixa latência (low‑latency).
  • Objetivo — pausas mínimas (milissegundos), mesmo em heaps enormes (até terabytes).
  • Operam praticamente totalmente em paralelo com o aplicativo.
  • Exigem Java 11+ (ZGC) ou Java 12+ (Shenandoah).
  • Indicados para sistemas críticos de latência (bolsas, fintech, análise em tempo real).
  • Flags de ativação: -XX:+UseZGC ou -XX:+UseShenandoahGC

3. Princípios de funcionamento dos GC modernos

Geração jovem e geração antiga (Young/Old Generation)

A JVM divide o heap em duas grandes partes:

Geração jovem (Young Generation): todos os novos objetos vão para cá. Aqui, a coleta acontece com frequência e rapidez ( Minor GC ).

Geração antiga (Old Generation, Tenured): para onde migram os objetos que “sobreviveram” a várias coletas na geração jovem. Aqui, a coleta é menos frequente, porém mais demorada ( Major/Full GC ).

Por quê? A maioria dos objetos em Java vive por muito pouco tempo (por exemplo, strings temporárias, coleções dentro de um método). Portanto, é possível coletar a geração jovem com rapidez e frequência, sem tocar a geração antiga.

Minor GC

  • Limpa apenas a geração jovem.
  • Rápido, com pausa curta.
  • Não afeta os objetos antigos.

Major (Full) GC

  • Limpa todo o heap (tanto a geração jovem quanto a antiga).
  • Pode levar muito tempo (segundos ou mais em heaps grandes).
  • Geralmente é acompanhado por uma pausa longa do aplicativo.

Como o GC decide quais objetos remover?

O GC procura objetos “vivos” a partir do conjunto de raízes (root set): variáveis locais nas pilhas das threads, campos estáticos, parâmetros de métodos etc. Tudo o que for alcançável é considerado vivo. O restante é lixo.

4. Comparação dos coletores modernos: G1, ZGC, Shenandoah

Vamos entender como se diferenciam os GC mais modernos e populares. Para isso, segue uma tabela ilustrativa:

Coletor Objetivo principal Modelo de memória Pausas mínimas Escalabilidade Suporte Quando usar
G1 Equilíbrio entre pausas e desempenho Regiões ~10–200 ms Até centenas de GB Java 9+ (padrão) A maioria dos aplicativos de servidor
ZGC Pausa mínima Regiões, “marcas coloridas” <10 ms Até terabytes Java 11+ Tempo real, crítico de latência
Shenandoah Pausa mínima Regiões, “marcas coloridas” <10 ms Até terabytes Java 12+ (Red Hat) Tempo real, crítico de latência

G1 GC: Garbage First

  • Divide o heap em muitas regiões (geralmente 1–32 MB cada).
  • Durante a coleta, escolhe as regiões com mais lixo (“garbage first”).
  • Pode coletar apenas uma parte do heap, não tudo de uma vez.
  • Permite definir a pausa alvo: -XX:MaxGCPauseMillis=200.
  • Adequado para equilibrar desempenho e pausas; usado por padrão desde o Java 9.

Exemplo de ativação (se por acaso estiver desativado):

java -XX:+UseG1GC -jar myapp.jar

ZGC: Z Garbage Collector

  • Experimental no Java 11, estável a partir do Java 15.
  • Quase não para o aplicativo: as pausas geralmente são <10 ms, mesmo com 1–2 TB de heap.
  • Usa “marcas coloridas” (coloring) e ponteiros especiais.
  • Requer uma JVM de 64 bits; não funciona em sistemas de 32 bits.
  • Suportado em Linux, macOS e Windows.

Exemplo de ativação:

java -XX:+UseZGC -jar myapp.jar

Shenandoah

  • Desenvolvido pela Red Hat; objetivos semelhantes aos do ZGC.
  • Pausas mínimas, trabalho paralelo ativo com o aplicativo.
  • Suporte para Linux e Windows; parte de builds do OpenJDK.
  • Usa técnicas semelhantes, mas algoritmos internos diferentes.

Exemplo de ativação:

java -XX:+UseShenandoahGC -jar myapp.jar

Comparação visual

graph TD
    A[Geração jovem] -->|Minor GC| B[Geração antiga]
    B -->|Major GC| C[Pausa do GC]
    D[G1: regiões] --> E[Regiões selecionadas]
    F[ZGC/Shenandoah: regiões] --> G[Coleta paralela]

5. Prática: como descobrir e alterar o GC

Como descobrir qual GC está sendo usado?

  1. Logs da JVM: Inicie o aplicativo com os parâmetros -Xlog:gc* (Java 9+) ou -verbose:gc (até o Java 8). Nos logs será possível ver qual GC está em uso e com que frequência ocorrem as pausas.
  2. jcmd: Execute:
    jcmd <pid> VM.flags
    
    onde <pid> é o identificador do processo Java.
  3. jvisualvm: Na seção “Monitoramento” é possível ver o tipo de GC.

Como alterar o GC do seu aplicativo?

Adicione o flag necessário ao iniciar o programa Java:

G1 GC (padrão, pode ser indicado explicitamente):

java -XX:+UseG1GC -jar myapp.jar

ZGC:

java -XX:+UseZGC -jar myapp.jar

Shenandoah:

java -XX:+UseShenandoahGC -jar myapp.jar

Como definir o tamanho do heap e as pausas?

  • Tamanho máximo do heap: -Xmx2G
  • Tamanho mínimo do heap: -Xms512M
  • Para G1: pausa desejada — -XX:MaxGCPauseMillis=200

Exemplo de execução completa:

java -Xms512M -Xmx2G -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar myapp.jar

6. Particularidades da escolha do GC para diferentes tarefas

Quando escolher o G1

  • Na maioria dos aplicativos de servidor e desktop — excelente escolha padrão.
  • Funciona bem com heaps de centenas de megabytes a centenas de gigabytes.
  • Equilibra desempenho e pausas.

Quando escolher ZGC ou Shenandoah

  • Se o aplicativo for sensível à latência (crítico de latência: bolsas, jogos on-line, análise em tempo real).
  • Se o heap for enorme (centenas de gigabytes ou mais).
  • Se apenas pausas mínimas forem aceitáveis (milissegundos).
  • Exigem Java 11+ (ZGC) ou Java 12+ (Shenandoah).

Quando o Parallel GC é suficiente

  • Para aplicativos pequenos onde a vazão máxima é mais importante e as pausas não são críticas.
  • Para processamento em lote (batch), onde é possível “aguentar” uma parada de Full GC.

7. Exemplo: comparando o comportamento de GC em um aplicativo simples

Um pequeno aplicativo que gera muitos objetos temporários (simulação de processamento de pedidos):

public class GCSimulator {
    public static void main(String[] args) {
        while (true) {
            // Criamos 100.000 objetos a cada ciclo
            for (int i = 0; i < 100_000; i++) {
                String s = new String("Order-" + i);
            }
            // Damos uma pequena pausa
            try { Thread.sleep(100); } catch (InterruptedException e) {}
        }
    }
}

Execute-o com diferentes GC e observe os logs:

java -Xmx256M -XX:+UseG1GC -Xlog:gc* GCSimulator
java -Xmx256M -XX:+UseZGC -Xlog:gc* GCSimulator

O que você verá?
O G1 fará pausas frequentes, porém curtas. ZGC/Shenandoah — pausas ainda mais curtas, mas podem ocorrer com maior frequência. Parallel GC — pausas mais longas, porém mais raras.

8. Erros comuns e nuances ao trabalhar com GC

Erro nº 1: Esperar que o GC resolva todos os problemas de memória. O GC não é uma varinha mágica. Se você mantiver referências para objetos desnecessários, nenhum GC vai ajudar — haverá vazamento de memória.

Erro nº 2: Chamar System.gc() explicitamente. A JVM sabe melhor quando coletar lixo. Forçar o GC pode causar uma pausa longa e reduzir o desempenho.

Erro nº 3: Ignorar os logs do GC. Se você não observar os logs do GC, pode não perceber que seu aplicativo “congela” regularmente em Full GC.

Erro nº 4: Usar GC obsoletos. Por exemplo, CMS não é mais desenvolvido. É melhor migrar para o G1 ou para coletores modernos de baixa latência.

Erro nº 5: Escolher o GC errado para a tarefa. Se você tem um aplicativo crítico de latência e usa o Parallel GC — espere pausas longas. Se for processamento em lote e você ativar o ZGC — terá sobrecarga desnecessária.

1
Tarefa
JAVA 25 SELF, nível 64, lição 1
Bloqueado
Descobrindo qual coletor de lixo é utilizado – Detetive da JVM 🕵️
Descobrindo qual coletor de lixo é utilizado – Detetive da JVM 🕵️
1
Tarefa
JAVA 25 SELF, nível 64, lição 1
Bloqueado
Ajuste do coletor de lixo e configuração de pausas – Perfeccionista Configurações de Desempenho ⚙️
Ajuste do coletor de lixo e configuração de pausas – Perfeccionista Configurações de Desempenho ⚙️
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION