Garbage Collector – Conceitos – .NET Framework

Neste primeiro artigo sobre o Garbage Collector vamos ver alguns conceitos importantes para entender o funcionamento deste gerenciador de memória, um dos pilares do .NET Framework.

Quando programamos em C ou C++, muitos objetos requerem que façamos a alocação de seus recursos em sua declaração, antes dos objetos poderem ser utilizados com segurança. Liberar estes recursos de volta ao pool de memória livre também é responsabilidade do programador. Se os recursos não forem liberados, dizemos que o código está com vazamento de memória e mais recursos serão consumidos sem necessidade. Por outro lado, se os recursos forem liberados prematuramente, perda de dados, áreas de memória corrompidas e erros de ponteiros nulos podem ocorrer.

Java e C# previnem estes perigos gerenciando independentemente o tempo de vida de todos os objetos em uso pelo aplicativo.

No Java, o JVM toma conta de liberar memória não utilizada mantendo registro de referências para os recursos alocados. Assim que o JVM detecta que um recurso não é mais referenciado, o recurso é coletado e jogado fora.

Em C#, o Garbage Collector é responsabilidade da CLR (Common Language Runtime) com funcionamento parecido com o JVM. O Garbage Collector checa periodicamente o heap de memória por qualquer objeto sem referência e libera os recursos vinculados a esses objetos.

 

Coleta Automática (Garbage Collector) no .NET

Quando a Microsoft decidiu por uma nova geração de plataforma chamada .NET, sua primeira intenção foi criar uma linguagem fácil de aprender e ter um grande conjunto de APIs. Houve um grande esforço também na coleta de lixo (Garbage Collector) através deste modelo de coleta automática de lixo.

O Garbage Collector foi implementado em uma thread em separado, sempre rodando no back end.

Mas rodar uma thread separada não dá overhead extra? Sim, com certeza. É por isso que a thread do Garbage Collector tem a prioridade mais baixa. Mas quando o Sistema não encontra mais espaço no heap gerenciado, a thread do Garbage Collector recebe prioridade REALTIME (é a mais alta prioridade no Windows) e sai recolhendo todos os objetos que não são mais utilizados.

 

Benefícios do Garbage Collector

  • Você pode desenvolver seu aplicativo sem se preocupar em liberar a memória toda vez que terminar de utilizar um determinado objeto.
  • A alocação de objetos no heap gerenciado é feito com o máximo de eficiência.
  • Recolhe objetos que não estão sendo utilizados e limpa a memória.
  • Mantém os objetos em memória em seu devido lugar, impedindo que um objeto utilize conteúdo de outro objeto.

 

O heap gerenciado

Depois que o Garbage Collector é inicializado pelo CLR (Commom Language Runtime), ele aloca um segmento de memória para armazenar objetos. Esta memória é chamada de heap gerenciado.

Existe um heap gerenciado para cada processo gerenciado. Todas as threads de um processo alocam memória no mesmo heap.

 

Observação Técnica: O tamanho dos segmentos alocados pelo Garbage Collector é específico de sua implementação e está sujeita a alterações a qualquer momento, incluindo em atualizações periódicas. Seu aplicativo nunca deve fazer suposições sobre ou depender do tamanho de um segmento específico. Muito menos tentar configurar a quantidade de memória disponível para alocação.

 

Quanto menos objetos alocados no heap, menos trabalho o Garbage Collector terá. Por isso, quando você alocar objetos, tente alocar apenas a quantidade de memória necessária.

Quando uma coleta é disparada, o Garbage Collector limpa a memória ocupada por objetos sem uso. Este processo “compacta” objetos em uso, os reorganiza para ficarem juntos e o espaço excedente é removido. Assim o heap fica menor, ocupando menos memória. Isto assegura que objetos alocados juntos continuem juntos no heap gerenciado, preservando sua localização.

A frequência e duração da intervenção das coletas é resultado do volume de alocações e da quantidade de memória restante no heap gerenciado.

O heap é dividido em dois: uma parte com objetos grandes (a partir de 85Kbytes, normalmente arrays) e outro com os menores.

 

Gerações

O heap é organizado em gerações para poder lidar com objetos de vida útil longa e objetos de vida útil curta. A coleta ocorre primeiro com os objetos de vida útil curta, que normalmente ocupam uma pequena parte do heap. Existem 3 gerações de objetos no heap:

  • Geração 0: A mais jovem das gerações e contém objetos de vida útil curta. Um exemplo de objeto de vida curta são variáveis temporárias. A coleta ocorre mais frequentemente nesta geração.

Objetos novos alocados formam uma nova geração de objetos e são implicitamente de geração 0, a não ser que sejam objetos grandes. Neste caso eles vão diretamente para o heap de grandes objetos na geração 2.

A maioria dos objetos são coletados na geração 0 e não sobrevivem para a próxima geração.

  • Geração 1: Esta geração contém objetos de vida curta e serve como um buffer entre objetos de vida curta e objetos de vida longa.
  • Geração 2: Contém objetos de vida longa. Um exemplo deste tipo é um objeto de uma aplicação servidor que contém dados estáticos que se mantém ativos durante o processo.

A coleta ocorre em gerações específicas quando as condições permitem. Coletar uma geração significa coletar objetos nesta geração e em todas as gerações mais novas. Uma coleta na geração 2 também é conhecida como uma coleta total, pois recolhe todos os objetos de todas as gerações (ou seja, todos os objetos do heap gerenciado).

 

Sobreviventes e promoções

Objetos não recolhidos durante uma coleta são conhecidos como sobreviventes e são promovidos para a próxima geração. Objetos sobreviventes de uma coleta na geração 0 são promovidos para a geração 1, objetos sobreviventes de uma coleta na geração 1 são promovidos para a geração 2 e objetos que sobreviventes a uma coleta na geração 2 permanecem na geração 2.

Quando o Garbage Collector percebe que a taxa de sobrevivência em uma geração é alta, ele aumenta o limite de alocações para esta geração, de modo que a próxima coleta consegue uma quantidade substancial de memória recuperada. O CLR faz o balanço continuamente de duas prioridades: não deixar que o conjunto de trabalho de um aplicativo fique muito grande e não deixar a coleta tomar muito tempo.

 

Gerações e segmentos efêmeros

As gerações 0 e 1 também são conhecidos como gerações efêmeras, pelo fato dos objetos neles serem de vida curta.

Gerações efêmeras devem ser alocados no segmento de memória que é conhecido como segmento efêmero. Cada novo segmento adquirido pelo Garbage Collector se torna um novo segmento efêmero e contém os elementos que sobreviveram a coleta da geração 0. O antigo segmento efêmero se torna o novo segmento geração 2.

O tamanho do segmento efêmero varia dependendo do sistema (32 ou 64 bits) e do tipo de Garbage Collector que está em execução. Valores padrão estão na tabela seguinte.

 

32-bit 64-bit
GC de estação 16 MB 256 MB
GC de servidor 64 MB 4 GB
GC de servidor com mais de 4 CPUs lógicos 32 MB 2 GB
GC de servidor com mais de 8 CPUs lógicos 16 MB 1 GB

 

Segmentos efêmeros podem incluir objetos geração 2. Objetos geração 2 podem utilizar múltiplos segmentos (quantas seu processo precisar e a memória suportar).

A quantidade de memória liberada é proporcional ao espaço ocupado pelos objetos inativos.

Agora que estudamos um pouco dos conceitos do Garbage Collector, conseguiremos entender melhor seu funcionamento no próximo artigo da série.

Lembrando que a fanpage do Celso no Facebook também tem conteúdo sobre empreendedorismo!

Comece pelo e-book GRATUITO

5 Passos Para Desenvolver Alexa Skills

Se você não sabe por onde começar, este e-book te mostra os passos para ser um desenvolvedor de skills de sucesso.
100% livre de spam.

Para enviar seu comentário, preencha os campos abaixo:

Deixe uma resposta

*

Seja o primeiro a comentar!