WinDBG: Memory II - Garbage Collector I

Neste primeiro post sobre GC, você irá compreender seu objetivo e o conceito de gerações.

Enfim o tão esperado Garbage Collector. Apesar de não ser um conceito novo, muitas pessoas desconhecem sua real função e estrutura. Na minha opinião, conhecer o Garbage Collector deveria ser tarefa básica de todos desenvolvedor. Isso mesmo, desenvolvedor! Se o responsável por alocar objetos não conhece o funcionamento deste poderoso recurso, como poderá ter certeza que está tratando a memória e os objetos da melhor maneira possível?

O que é Garbage Collector?

A idéia de Garbage Collector não é nova, e muito menos pertence a uma determinada tecnologia. Em muitas conversas de Java x .NET (uma besteira imensa) já ouvi pessoas dizendo ".NET copiou o GC do Java".

O conceito de garbage collector (GC) nasceu em 1959 por John McCarthy, para resolver problemas do Lisp.
Seu objetivo principal é fornecer um mecanismo automático para gerenciamento de memória, removendo objetos que não serão mais acessados na aplicação. A idéia é deixar a responsabilidade de manter a memória consistente para um mecanismo automático.

Antigamente, esta responsabilidade era do desenvolvedor, que criava a objeto e removia-o da memória.
De cara podemos citar dois problemas:

    1. O desenvolvedor poderia simplesmente "esquecer" de remover o objeto da memória; ou
    2. poderia remover o objeto no momento errado, enquanto ainda havia ponteiros para ele.

Esta responsabilidade gerava uma esforço muito grande, o que impactava diretamente no prazo, qualidade e testes de todos os projetos.

Conceito: Heap

O Heap é um gerenciador de memória utilizado para alocar e liberar memória dinamicamente. Normalmente, a utilização do Heap é realizada em situações onde a quantidade de memória necessária é desconhecida, ou muito grande para ser armazenada no stack.

Mecanismo

Existem muitos algoritmos para um Garbage Collector hoje em dia, e todos são dependentes do sistema operacional. Os GC trabalham intimamente com o Gerenciador de Memória do sistema. Por isso, é importante saber que o GC do CLR não é igual ao GC da JVM.

Nestes posts iremos trabalhar apenas com o GC do CLR.

  • Managed Heap

Toda alocação e liberação de memória em uma aplicação .NET é realizada através do Garbage Collector. Por este motivo, o conceito de Managed Heap será aplicado até o fim deste post. O GC gerencia todo o processo de ciclo de vida dos objetos, visando a melhor manutenção da aplicação e tentando evitar problemas de memory leaks, hangs ou crashes.

Quando um processo .NET é iniciado, o sistema operacional aloca um espaço livre de memória, chamado de managed heap. Além disso, o heap mantem um ponteiro de memória chamado NextObjPtr. Este ponteiro indica o local onde o próximo objeto deve ser alocado. A utilização deste ponteiro é básica: quando um novo objeto é criado através da instrução new, é realizada a verificação de espaço para sua alocação. Se existe espaço suficiente, o objeto é criado no endereço apontado pelo NextObjPtr, e este endereço é retornado ao final do construtor.

Ao final deste processo, o NextObjPtr aponta para um novo endereço de memória disponível.

  • Gerações

O Managed Heap é dividido em gerações, Gen 0, Gen 1 e Gen 2, além de uma área para objetos grandes (acima de 85Kb). Cada geração desta armazena objetos de "idades" diferentes, do mais novo (Gen 0) para o mais "antigo" (Gen 2). Mas antes, é necessário entender algumas suposições importantes sobre o GC:

    1. Quanto maior o objeto na memória, maior a probabilidade de ter uma vida mais longa. Essa suposição assume que, quanto maior o objeto, mais trabalho existirá para criá-lo, e programadores vão utilizá-lo quando necessário e por mais tempo, como cache;
    2. Quanto mais tempo um objeto permanece vivo, a probabilidade de exisitir mais tempo aumenta;
    3. Todos os objetos são inválidos, até que provem que não.

Por esse motivo as gerações são utilizadas.

Geração 0 Este heap é o menor de todos, normalmente com 256Kb. É utilizado para armazenar objetos recem criados. Normalmente os objetos neste geração não passam para as próximas.
Geração 1 Todos os objetos que existiam na Geração 0 e sobrevivem a uma limpeza são "enviados" para a geração 1. Este Heap contém normalmente 2Mb.
Geração 2 Os objetos que continuam existindo após uma limpeza da geração 1 são "enviadas" para a geração 2 (10 Mb).  Os objetos deste heap são removidos apenas em uma limpeza total do GC.
Large Objects Heap (LOH) Qualquer objeto maior que 85 Kb será armazenado neste heap, e será removido apenas em uma limpeza total pelo GC.

Quando o GC inicia um processo de limpeza, todos os objetos da geração 0 que contém alguma raiz são enviados para a geração 1, pois são considerados vivos. Após este processo, a geração 0 fica vazia.

Agora algumas questões que vocês podem estar se perguntando:

Quando uma limpeza ocorre? Quando o GC irá executar?

O GC irá rodar quando uma nova alocação precisa ser realizada e não há espaço suficiente para isso na gen 0. Nesse caso, o GC executa a limpeza na geração 0. Se alocações por conta desta limpeza ocorrem na geração 1 (pelo GC) e também não há espaço, uma limpeza na geração 1 é realizada, e o mesmo ocorre com a geração 2.

IMPORTANTE: Objetos que permanecem vivos depois de uma limpeza sempre vão para a geração seguinte.

LEMBRE-SE: O GC sempre irá limpar apenas a geração 0, sem olhar para objetos com ou sem raiz nas geração seguintes. Isso só irá ocorrer quando a limpeza da geração 0 não liberar espaço suficiente para uma nova alocação.

Outra maneira do GC executar é invocando o método GC.Collect(). Mas isso será um questão para depois. O importante é não utilizar isso, quase nunca!

--

Referências:

http://msdn.microsoft.com/en-us/library/ms973837.aspx

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

http://blogs.msdn.com/tess/archive/2007/04/10/net-garbage-collector-popquiz-followup.aspx

http://grounding.co.za/blogs/brett/archive/2007/07/16/garbage-collection-basics.aspx

Livro Advanced Windows Debugging, de Mario Hewardt e Daniel Pravat;

--

O próximo post irá introduzir conceitos de execução, finalização e dipose, para então partir para WinDBG em Memory Leaks.

Abraços!

1 Comment

Comments have been disabled for this content.