← Todos os artigos

Garbage Collectors da JVM: G1, ZGC e quando cada um faz sentido

Um tour pelos coletores de lixo modernos da JVM — como funcionam, qual o trade-off entre throughput e latência, e como escolher o certo para sua carga.

A maior parte dos desenvolvedores Java trata o garbage collector como uma caixa-preta — até a primeira pausa de GC de 2 segundos em produção. Entender o que acontece por baixo é o que separa “aumentei o heap e rezei” de uma decisão de tuning consciente. Vamos abrir a caixa.

O problema fundamental

Todo GC precisa equilibrar três coisas que estão em tensão:

  • Throughput — quanto do tempo de CPU é trabalho útil vs. coleta.
  • Latência — o tamanho das pausas (stop-the-world) que a aplicação sofre.
  • Footprint — quanta memória e CPU extra o coletor consome.

Você não otimiza os três ao mesmo tempo. Escolher um GC é escolher qual eixo sacrificar.

G1 — o padrão pragmático

Desde o Java 9, o G1 (Garbage-First) é o coletor padrão. Ele divide o heap em regiões de tamanho fixo e coleta primeiro as que têm mais lixo — daí o nome. O objetivo dele é cumprir uma meta de pausa que você define:

// Mira pausas de no máximo ~50ms; o G1 ajusta o resto sozinho.
// -XX:+UseG1GC -XX:MaxGCPauseMillis=50

G1 é a escolha segura para a maioria das aplicações de servidor: bom throughput, pausas previsíveis na casa das dezenas de milissegundos.

ZGC — quando a latência é sagrada

O ZGC foi desenhado para um único objetivo: pausas sub-milissegundo, mesmo em heaps de centenas de gigabytes. Ele faz quase todo o trabalho concorrentemente com a aplicação, usando colored pointers e load barriers para mover objetos sem parar o mundo.

// Pausas que não crescem com o tamanho do heap.
// -XX:+UseZGC

O trade-off: ZGC consome mais CPU e memória que o G1. Você paga em throughput e footprint para comprar latência. Faz sentido para sistemas onde uma pausa de 100ms é inaceitável — trading de baixa latência, APIs com SLA agressivo de p99.

Como escolher

Um guia rápido, sem dogma:

CargaColetor
App de servidor típicoG1 (padrão)
Latência p99 crítica, heap grandeZGC
Batch / throughput puroParallel GC

Meça antes de tunar

A regra de ouro: não troque de GC no escuro. Ligue os logs e olhe o comportamento real antes de mexer em qualquer flag:

# Logs unificados de GC a partir do Java 9+
java -Xlog:gc*:file=gc.log:time,uptime -jar app.jar

Ferramentas como o GCeasy transformam esse log em gráficos de pausa e throughput. Na imensa maioria dos casos, o G1 com um MaxGCPauseMillis razoável já resolve — e você descobre isso medindo, não adivinhando.