Como o Java gerencia a memória: não é apenas "coleta de lixo"
\n\nMuitos desenvolvedores que migram para Java vindos de C++ ou Python inicialmente se alegram: "Ufa, não preciso me preocupar com vazamentos de memória!". Mas depois recebem um OutOfMemoryError no momento mais inoportuno e percebem que o gerenciamento de memória em Java exige um entendimento profundo. Hoje vamos olhar sob o capô da JVM e analisar como realmente funcionam a memória e o garbage collector em Java.
Este artigo será útil tanto para iniciantes que desejam entender o Java memory model, quanto para desenvolvedores experientes que buscam formas de otimizar o garbage collection Java. Vamos lá!
\n\n\n\n
Como a memória em Java é organizada: heap, stack e Metaspace
\n\nAntes de falar sobre o garbage collector Java, é preciso entender onde exatamente seus objetos "vivem". A JVM divide a memória em várias áreas principais:
\n\n- \n
- Heap (pilha de objetos) — aqui são armazenados todos os objetos criados. É o campo principal de atuação do garbage collection Java. \n
- Stack (pilha de execução) — armazena tipos primitivos e referências para objetos no heap. Cada thread tem sua própria stack. \n
- Metaspace — substituiu o PermGen no Java 8, armazena metadados das classes. \n
- Code Cache — código compilado pelo compilador JIT. \n
Aqui está um exemplo simples mostrando para onde os dados vão:
\n\npublic class MemoryDemo {\n public static void main(String[] args) {\n int number = 42; // Primitivo — na stack\n String text = "Hello"; // Referência na stack, objeto no heap\n List<String> list = new ArrayList<>(); // Referência na stack, lista no heap\n list.add("Java"); // String "Java" — também no heap\n }\n}\n\n\nPonto chave: todos os objetos criados com new vão para o heap Java. É essa área que o garbage collector em Java "monitora".
\n\n
Por que o garbage collector não é um inimigo, mas um amigo (se você entender sua lógica)
\n\nMuitos desenvolvedores temem o Java GC por causa das pausas "Stop-The-World". Mas vamos encarar a verdade: sem o gerenciamento automático de memória Java, teríamos que escrever manualmente free() e lidar com segfaults. O garbage collector Java não é um demônio maligno, mas um mecanismo complexo e inteligente.
Princípios básicos de funcionamento do garbage collection Java:
\n\n- \n
- Um objeto é considerado "lixo" se não houver referências ativas a ele a partir da stack ou de campos estáticos. \n
- O GC não pode remover um objeto que ainda possui uma referência (mesmo que você tenha esquecido dela). \n
- Diferentes algoritmos de GC têm objetivos distintos: pausas mínimas, máxima taxa de transferência ou economia de memória. \n
Aqui está um exemplo clássico de vazamento de memória, quando um objeto é "esquecido" em uma coleção:
\n\npublic class LeakExample {\n private static List<byte[]> cache = new ArrayList<>();\n\n public static void addToCache() {\n byte[] data = new byte[1024 * 1024]; // 1 MB\n cache.add(data); // A referência permanece — o GC não toca\n }\n\n public static void main(String[] args) {\n for (int i = 0; i < 1000; i++) {\n addToCache();\n }\n // Aqui ocorrerá OutOfMemoryError, embora os objetos não sejam mais necessários\n }\n}\n\n\nConclusão: a memória em Java não será liberada enquanto você mantiver uma referência. Entender isso é o primeiro passo para escrever código eficiente.
\n\n\n\n
Tipos de garbage collectors em Java: do Serial ao ZGC
\n\nO Java moderno oferece várias implementações de garbage collection Java. A escolha depende das suas necessidades:
\n\n1. Serial GC — para aplicações pequenas
\nColetor single-thread que "congela" toda a aplicação. Usado em