#java
Garbage Collector no Java: Entenda como a JVM gerencia memória

Fernanda C D Soares
engenheira de software · 30 de julho de 2026 · 7 min de leitura

Escrever código Java de alto desempenho vai além de apenas instanciar classes e criar métodos. O verdadeiro diferencial de um desenvolvedor está em compreender o que acontece nos bastidores — mais especificamente, como a Java Virtual Machine (JVM) lida com a alocação e liberação de recursos.
No centro do memory management em Java, existe um componente fundamental
chamado Garbage Collector (GC). Se você já se perguntou para onde vão os
objetos que você cria ou por que o famoso java.lang.OutOfMemoryError
acontece, este guia foi feito para você.
Por que precisamos de um Garbage Collector?
Em linguagens mais antigas ou de baixo nível (como C ou C++), o gerenciamento
de memória é manual. O desenvolvedor é responsável por alocar espaço
(malloc) e, obrigatoriamente, liberá-lo (free) quando o objeto não for
mais necessário.
Esquecer de liberar memória gera o chamado Memory Leak (vazamento de memória). Por outro lado, tentar acessar ou liberar um ponteiro já excluído pode quebrar a aplicação imediatamente.
O Garbage Collector no Java surge para resolver essa dor. Ele é um processo automatizado da JVM que identifica objetos que não estão mais em uso e recupera a memória alocada para eles, evitando falhas humanas e permitindo que você foque no que realmente importa: a lógica de negócio.
Como a memória funciona na JVM
Para entender como funciona o Garbage Collector, primeiro precisamos entender como a JVM organiza a memória da sua aplicação. A memória de execução é dividida principalmente em duas áreas estratégicas: Stack e Heap.
Stack x Heap
- Stack (Pilha): armazena as chamadas de métodos, variáveis locais e primitivos. Ela segue a estrutura LIFO (Last In, First Out). Cada thread tem sua própria Stack. Quando o escopo de um método termina, tudo o que estava na Stack para aquele método é limpo instantaneamente.
- Heap: é o espaço compartilhado entre todas as threads onde os objetos
reais residem. Quando você faz um
new User(), o objetoUseré criado na Heap, enquanto a variável de referência que aponta para ele fica armazenada na Stack.
O ciclo de vida de um objeto
Tudo no Java começa com a criação de um objeto na memória Heap. Mas o que acontece quando esse objeto não tem mais utilidade?
- Criação: o objeto é alocado na Heap.
- Uso: a aplicação utiliza o objeto através de referências ativas (na Stack ou dentro de outros objetos).
- Isolamento: o objeto perde todas as suas referências (por exemplo, a
variável sai de escopo ou recebe
null). - Elegibilidade: o objeto se torna elegível para GC.
- Coleta: o Garbage Collector passa, identifica o objeto morto e limpa o espaço que ele ocupava na Heap.
Como o Garbage Collector descobre que um objeto morreu?
O GC não adivinha. Ele usa um conceito chamado GC Roots (Raízes do Garbage Collector) e um algoritmo de rastreamento de acessibilidade (Tracing Garbage Collection).
A JVM mapeia tudo a partir de raízes conhecidas, como:
- variáveis locais da Stack ativas
- threads em execução
- variáveis estáticas de classes (
static)
Se o GC parte dessas GC Roots e constrói uma árvore de navegação, qualquer objeto que não possa ser alcançado por esse caminho é considerado morto.
Mesmo que dois objetos façam referência um ao outro (referência circular), se a cadeia deles não estiver conectada a nenhuma GC Root, o Java GC os marcará para remoção.
O que acontece durante uma coleta?
Geralmente, o processo de coleta segue o modelo clássico Mark-Sweep-Compact:
- Mark (Marcar): o GC percorre a árvore de objetos a partir das GC Roots e marca tudo o que está "vivo".
- Sweep (Varrer): ele limpa a memória apagando os objetos que não foram marcados como vivos.
- Compact (Compactar): para evitar que a Heap fique cheia de "buracos" vazios (fragmentação de memória), o GC reorganiza os objetos restantes deixando-os contíguos na memória.
Durante algumas etapas da coleta, a JVM pode pausar momentaneamente a execução dos threads da sua aplicação. Essa pausa é conhecida como Stop-The-World (STW).
A hipótese weak generational e as gerações da Heap
Estudos sobre o comportamento de aplicações orientadas a objetos mostraram um padrão curioso: a grande maioria dos objetos morre logo após ser criada (variáveis temporárias, DTOs, retornos de métodos).
Por isso, a Heap é dividida em gerações:
Young Generation (Geração Jovem)
É onde os novos objetos nascem. É subdividida em:
- Eden Space: onde todo objeto é recém-alocado.
- Survivor Spaces (S0 e S1): espaços intermediários para onde os objetos sobreviventes vão após uma limpeza no Eden.
Old Generation (Geração Antiga / Tenured)
Se um objeto sobrevive a vários ciclos de limpeza na Young Generation, a JVM entende que ele é um objeto "de longa vida" (como caches, singletons, configurações) e o promove para a Old Generation.
Entendendo as coletas: Minor, Major e Full GC
A divisão em gerações permite que o garbage collector Java trabalhe de forma muito mais eficiente através de diferentes tipos de execução:
Minor GC
Ocorre exclusivamente na Young Generation. Como a taxa de mortalidade de objetos nessa região é altíssima, o Minor GC é extremamente rápido e causa impactos insignificantes na aplicação.
Major GC
Ocorre na Old Generation. É mais demorado, pois limpa objetos maiores e em uma área com taxa de retenção mais alta.
Full GC
É a limpeza completa: limpa tanto a Young quanto a Old Generation (e outras áreas internas da JVM). O Full GC é um evento custoso, gerando pausas Stop-The-World mais longas. Se sua aplicação está fazendo Full GC com muita frequência, há grandes chances de haver gargalos de performance ou vazamentos de memória.
Quais Garbage Collectors existem hoje?
A JVM evoluiu bastante e hoje oferece diferentes algoritmos de GC, otimizados para cenários específicos:
- Serial GC: usa uma única thread. Indicado para aplicações pequenas, CLI ou ambientes limitados (como containers micro).
- Parallel GC: focado em throughput (vazão de dados). Usa múltiplas threads para gerenciar a memória. É o padrão em algumas versões mais antigas do Java.
- G1 GC (Garbage-First): padrão a partir do Java 9 para máquinas de médio/grande porte. Divide a Heap em várias regiões independentes e foca em atender metas de tempo de pausa pré-definidas.
- ZGC (Z Garbage Collector) & Shenandoah GC: algoritmos modernos de baixíssima latência (Ultra-Low Latency). Capazes de gerenciar Heaps gigantescas (terabytes) mantendo pausas de Stop-The-World na casa dos milissegundos.
Como descobrir qual GC sua aplicação utiliza?
Você não precisa adivinhar qual GC está rodando em ambiente de produção ou desenvolvimento. É fácil verificar via linha de comando rodando:
java -XX:+PrintCommandLineFlags -versionProcure no retorno do terminal por flags como -XX:+UseG1GC,
-XX:+UseParallelGC ou -XX:+UseZGC.
Também é possível monitorar o comportamento do GC em tempo real utilizando ferramentas como:
- VisualVM
- JConsole
- JDK Mission Control (JMC)
Mitos sobre Garbage Collector
"Chamar System.gc() força o Garbage Collector a rodar"
Mito. A instrução System.gc() apenas faz uma sugestão à JVM de que seria
um bom momento para limpar a memória. A JVM pode simplesmente ignorar a
chamada. Além disso, invocar esse método manualmente no seu código é
considerado uma má prática, pois pode disparar um Full GC desnecessário.
"Não existem vazamentos de memória no Java"
Mito. Embora o GC impeça o esquecimento de alocações sem referência, você ainda pode criar Memory Leaks lógicos. Se você adicionar objetos a uma lista estática e nunca removê-los, eles permanecerão conectados a uma GC Root para sempre, impedindo a coleta e lotando a Heap.
Conclusão
Um dos maiores diferenciais de quem desenvolve em Java é entender que escrever código não significa apenas criar objetos. Significa compreender como a JVM administra esses objetos durante toda a vida da aplicação.
O Garbage Collector existe justamente para automatizar esse trabalho, liberando memória quando objetos deixam de ser utilizados e permitindo que possamos focar na lógica de negócio.
Mas entender como o Garbage Collector funciona vai muito além da curiosidade. Esse conhecimento ajuda a interpretar problemas de desempenho, investigar consumo excessivo de memória e tomar decisões mais conscientes ao desenvolver aplicações Java.
