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:
- O desenvolvedor poderia simplesmente "esquecer" de remover o objeto da memória; ou
- 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:
- 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;
- Quanto mais tempo um objeto permanece vivo, a probabilidade de exisitir mais tempo aumenta;
- 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!