Regra de ouro: O GC cuida de memoria gerenciada, mas nao libera recursos nao-gerenciados (files, sockets, handles OS, conexoes DB). IDisposable expoe Dispose() para liberar esses recursos deterministicamente β€” sem esperar o GC.

Ciclo de vida do objeto

usingsem using2+ coletasnew Object()UseDispose()GC collectsForgot Dispose?Finalizer runs (slow)using garante Dispose() mesmo com excecoes

IDisposable β€” por que existe?

O Garbage Collector libera memoria gerenciada automaticamente. Mas recursos do sistema operacional (file handles, sockets, conexoes de banco, mutexes) sao nao-gerenciados β€” o GC nao sabe libera-los. IDisposable.Dispose() e o contrato para cleanup deterministico.

AspectoRecurso GerenciadoRecurso Nao-Gerenciado
ExemplosObjetos .NET, strings, arraysFile handles, sockets, DB connections, OS handles
Quem libera?GC automaticamenteVoce via Dispose()
Quando libera?Indeterminado (quando GC rodar)Imediatamente no Dispose()
Consequencia do leakPressao de memoria (GC resolve)Exaustao de recursos β€” file locks, connection pool esgotado

GC Generations

GenerationO que contemColeta
Gen 0Objetos recem-criados (efemeros)Frequente e rapida
Gen 1Sobreviventes de Gen 0Menos frequente
Gen 2Objetos de vida longaRara e custosa (full GC)
Finalizer promove o objeto para Gen 1+ β€” o GC precisa de pelo menos 2 coletas para liberar. Isso e caro. Por isso GC.SuppressFinalize(this) e chamado em Dispose(): se voce ja limpou, nao precisa do finalizer.
Finalizer vs Dispose?
Dispose() e chamado pelo desenvolvedor (ou via using) β€” cleanup deterministico e imediato. Finalizer (~Type) e chamado pelo GC como backstop β€” nao-deterministico, lento, promove o objeto para Gen 1+. Na pratica: sempre chame Dispose(). Finalizer so existe como rede de seguranca caso alguem esqueca. Em codigo moderno, prefira SafeHandle que ja tem finalizacao critica embutida.