WinDBG: Memory II - Garbage Collector II

Neste novo post vou apresentar um pouco da execução do Garbage Collector, o que já será suficiente para iniciarmos os posts sobre memory leaks.

Geração? Pra que?

Primeira coisa que temos que entender: por que existem as gerações? Através do último post foi possível entender o que são… mas pra quê?

A razão de ter três gerações é a organização e performance. A geração 0 armazena objetos mais novos, que têm a tendência de serem removidos mais cedo do processamento. Este é o caso por exemplo de aplicações web em requisições por seus visitantes. Assim que a requisição termina, os objetos criados para atendê-la, em sua maioria, são marcados pelo GC como lixo.

Como explicado anteriormente, o algoritmo do GC para verificação de objetos percorre cada um procurando por raízes, que provem que o objeto ainda está “vivo”. É muito mais performático percorrer apenas alguns objetos, aqueles com maior probabilidade de serem removidos, do que todos os objetos da memória.

Esse é o objetivo das gerações :)

Execução

Ainda no conceito de gerações… quando criamos um objeto (new), um espaço de memória é armazenado para que este objeto possa ser criado. Este espaço, muito provavelmente, será armazenado na geração 0, por ser um objeto novo. Mas vamos supor que não exista espaço na geração 0. Neste caso, o GC fará uma limpeza neste geração e apenas nela, com o objetivo de remover objetos não utilizados e liberar espaço.

Depois de uma limpeza, como já apresentei, os objetos que sobrevivem em uma determinada geração são “promovidos” a uma outra. Portanto, uma limpeza na geração 0 marcará os objetos sobreviventes para a geração 1.

image_thumb

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

** No primeiro parágrafo do item Execução, eu citei que muito provavelmente um objeto novo será armazenado na geração 0. Devemos estar atento que objetos maiores que 85.000 bytes (85 Kb) serão armazenado diretamente em uma outra área, o Large Object Heap.

Para o GC, todos os objetos são inicialmente garbage. Em outras palavras, ele irá percorrer os objetos para tentar provar o contrário.
Em uma limpeza da geração 0 por exemplo, o GC percorre as raízes da aplicação até os objetos, construindo os grafos dos objetos. Se algum objeto, após esta construção, não tiver uma raiz correspondente, este objeto é considerado lixo, ou garbage.

O passo seguinte é remover os objetos não utilizados e liberar o espaço de memória. Após esta ação, a memória apresentará espaços entre espaços alocados e outros não. Portanto, o GC copia os objetos ainda ativos para a geração 1 (um diferente espaço de memória), liberando e mantendo contínuo o espaço para a geração 0.

Ver memcpy http://www.cplusplus.com/reference/clibrary/cstring/memcpy.html

Portanto, podemos resumir os passos até agora em:

  1. O GC percorre os objetos através das raízes da aplicação;
  2. Através destes caminhos, o GC cria um grafo com os objetos que foram acessados de alguma maneira através destas raízes;
  3. Os objetos que ficaram fora deste grafo são considerados lixo;
  4. O GC remove estes objetos da memória e copia os outros objetos ativos para um outro endereço de memória (memcpy), compactando-os;
  5. O GC libera o espaço da geração 0 e deixa a memória de forma contínua;

ms973837_dotnetgcbasics_02(en-us,MSDN_10) 

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

Bb985010_gcifig02(en-us,MSDN_10)

Bb985010_gcifig03(en-us,MSDN_10)
http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

Lembrando que a limpeza ocorre quando o Heap está cheio; isto vale para a geração 0, geração 1 ou geração 2. Quando uma limpeza na geração 2 ocorre, chamamos de Full Garbage Collector, já que todas as gerações anteriores a ela são verificadas também. Além disso, o full garbage collector ocasiona uma limpeza do LOH (Large Object Heap), o que pode custar muito caro para o processamento.

Quantos Heaps do .NET existem?

Existem 2 por processador lógico, 1 para objetos pequenos (SOH) e outro para objetos grandes (LOH). Em uma máquina core 2 duo, por exemplo, existem 4, 2 SOH e 2 LOH, um grupo para cada processador lógico.

A limpeza do Garbage Collector é, de alguma forma, ruim?

Não. O gerenciamento feito pelo GC é extremamente benéfico e implementado da melhor maneira possível.

Por que algumas pessoas dizem que executar o GC manualmente não é bom?

É muito difícil para os desenvolvedores saberem o melhor momento para executar o GC. Como já vimos, o processo de limpeza é muito pesado, e executá-lo a qualquer momento pode ser muito ruim para qualquer máquina. Para se ter uma idéia, quando GC está rodando, a utilização do CPU na máquina vai a 81%, sempre.

--

Espero que você tenha gostado deste post. A partir do próximo post vamos iniciar a série sobre memory leaks.

Obrigado e até a próxima!

No Comments