Memória em C# sob o microscópio: como funciona o coletor de lixo e por que isso é importante

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Magia que você é obrigado a entender

\n\n

Todo mundo que escreveu em C# por pelo menos algumas semanas sabe: a memória é gerenciada pelo ambiente de execução (CLR). Você simplesmente cria objetos com new, e quando eles não são mais necessários — o coletor de lixo (GC) vem e limpa tudo. Parece mágica, certo? Mas por trás dessa mágica existe uma engenharia rigorosa, e se você não a entender — sua aplicação vai começar a "travar", "consumir" gigabytes de RAM ou até cair com OutOfMemoryException.

\n\n

Neste artigo, vamos olhar sob o capô da CLR e entender exatamente como funciona o gerenciamento de memória em C#. Você aprenderá a diferença entre pilha e heap, o que são gerações de objetos, por que IDisposable é seu melhor amigo e como não pisar no ralo com o Large Object Heap. Vamos nessa!

\n\n

Pilha e heap: o fundamento que é omitido nos livros didáticos

\n\n

Quando seu programa está em execução, a CLR divide a memória em duas áreas principais: pilha (Stack) e heap (Heap). Entender a diferença entre elas é a base, sem a qual todo o resto perde o sentido.

\n\n

Pilha — rápida, mas pequena

\n

A pilha é a área de memória onde são armazenados value types (int, bool, struct, enum) e referências para objetos. Ela funciona pelo princípio LIFO (Last In, First Out). Quando você chama um método, um frame é alocado na pilha para ele. Assim que o método termina — o frame é destruído instantaneamente. Nenhum coletor de lixo é necessário aqui. A velocidade de acesso à pilha é máxima, mas o tamanho é limitado (geralmente 1-4 MB por thread).

\n\n
void Example()\n{\n    int x = 42;          // x está na pilha\n    string s = "Olá";    // referência s na pilha, o objeto em si — no heap\n}\n
\n\n

Heap — mais lento, mas mais espaçoso

\n

O heap (Managed Heap) é a memória "de longo prazo". Aqui vivem todos os reference types (classes, strings, arrays, delegates). A alocação de memória no heap ocorre a cada new. E é aqui que o coletor de lixo entra em ação. É ele quem decide quando a memória pode ser liberada e quando deve esperar.

\n\n

Importante: A pilha não é "ruim", e o heap não é "bom". Cada um tem sua função. Value types são rápidos, mas não podem ser passados por referência sem a palavra-chave ref. Reference types são flexíveis, mas exigem o GC.

\n\n

Como funciona o coletor de lixo em C#: gerações e algoritmo

\n\n

O coletor de lixo no .NET não é apenas "removeu e esqueceu". É um mecanismo inteligente que usa gerações (Generations) para minimizar pausas na execução da aplicação.

\n\n

Três gerações de objetos

\n
    \n
  • Gen 0 — objetos recém-nascidos. Vivem pouco tempo. A coleta da Gen 0 ocorre com frequência e é muito rápida.
  • \n
  • Gen 1 — "sobreviventes" após uma coleta. Zona de buffer entre objetos jovens e antigos.
  • \n
  • Gen 2 — objetos de longa duração. A coleta da Gen 2 é a mais cara e rara. Se um objeto chegou à Gen 2, provavelmente permanecerá lá por muito tempo.
  • \n
\n\n

O algoritmo de funcionamento do GC, de forma simplificada, é assim:

\n
    \n
  1. A aplicação solicita memória. Se não houver espaço no heap — a coleta é iniciada.
  2. \n
  3. O GC constrói um grafo de acessibilidade de objetos, começando pelas raízes (campos estáticos globais, variáveis locais nas pilhas das threads, registradores da CPU).
  4. \n
  5. Todos os objetos inacessíveis são marcados como "lixo".
  6. \n
  7. Ocorre a compactação (compaction) — objetos vivos são deslocados para evitar fragmentação.
  8. \n
  9. A memória liberada retorna ao pool.
  10. \n
\n\n

Aqui está um exemplo claro de como o GC afeta o desempenho:

\n\n
class HeavyObject\n{\n    public byte[] Data = new byte[1024 * 1024]; // 1 MB\n}\n\nvoid TestGC()\n{\n    for (int i = 0; i < 1000; i++)\n    {\n        var obj = new HeavyObject(); // Cada vez alocamos 1 MB no heap\n        // obj sai do escopo — vira lixo\n    }\n    // Após o loop, o GC pode coletar a Gen 0 não                

Blogs

Book Recommendations