Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Este artigo faz parte de uma série sobre o desempenho do aplicativo .NET. Os três primeiros artigos já estão disponíveis: Parte 01; Parte 02; Parte 03.
Neste artigo discutiremos as diretrizes do coletor de lixo. O Framework. NET utiliza o coletor de lixo automático para gerenciar a memória para todos os aplicativos. Quando você usa o operador New para criar um objeto, a memória do objeto é obtida a partir do heap gerenciado. Quando o coletor de lixo decide que o lixo acumulado já é o suficiente, ele executa uma coleta para liberar memória. Este processo é totalmente automático, mas há uma série de coisas que você precisa estar ciente.
Vamos dar uma olhada no ciclo de vida de um objeto gerenciado:
- A memória de um objeto é alocada a partir do heap gerenciado quando você chamar o *New*. O construtor do objeto é chamado após a memória ser alocada;
- O objeto é usado por um período de tempo;
- O objeto morre devido a todas as suas referências, seja explicitamente definidas, como null, ou então saindo do escopo;
Alocação
O heap gerenciado pode ser pensado como um bloco de memória contígua. Quando você cria um novo objeto, a sua memória é alocada na próxima posição disponível no heap gerenciado. Já que o coletor de lixo não precisa se procurar com espaço, as atribuições são extremamente rápidas, caso haja memória suficiente disponível. Se não houver para o novo objeto, o coletor de lixo tenta recuperar espaço para ele.
Coletor
Para recuperar o espaço, o coletor de lixo recolhe objetos que não estão mais acessíveis. Um objeto não é mais acessível quando não há referências a ele, todas elas são definidas como null, ou todas as referências a ele são de outros objetos, que podem ser coletados como parte do ciclo atual de coleta.
Quando uma coleta ocorre, os objetos acessíveis são traçados e marcados como recursos de rastreamento. O coletor de lixo recupera o espaço movendo objetos acessíveis no espaço contíguo e recuperando a memória usada pelos objetos inacessíveis. Qualquer objeto que sobrevive à coleta é promovido para a próxima geração.
Gerações
O coletor de lixo utiliza três gerações para agrupar objetos por sua vida útil e volatilidade:
- Geração 0 - Consiste em objetos recém-criados. Gen 0 é recolhido com frequência para garantir que objetos de vida curta sejam rapidamente limpos. Esses objetos que sobrevivem a uma coleta Gen 0 são promovidos a Geração 1;
- Geração 1 - Esta é coletada com menos frequência do que o Gen 0 e contém mais objetos de vida longa que foram promovidos a partir de Gen 0;
Orientações para coletores de lixo
Nesta seção iremos lhe dar algumas orientações para melhorar o desempenho da coleta de lixo.
O coletor de lixo é projetado para ser auto ajustar e ele ajusta sua operação para atender às necessidades do seu aplicativo com base na pressão de memória. Forçar programaticamente a coleta pode dificultar o ajuste e a operação do coletor de lixo.
void SomeMethod()
{
// Create a collection
ArrayList arr = new ArrayList(5);
// Create a custom object
MyObject mo = new MyObject();
// Create a WeakReference object from the custom object
WeakReference wk = new WeakReference(mo);
// Add the WeakReference object to the collection
arr.Add(wk);
// Retrieve the weak reference
WeakReference weakReference = (WeakReference)arr[0];
MyObject mob = null;
if( weakReference.IsAlive ) {
mob = (MyOBject)weakReference.Target;
}
if(mob==null) {
// Resurrect the object as it has been garbage collected
}
//continue because we have the object
}
- Não referenciar objetos de vida curta a partir dos objetos de longa duração;
- Evite implementar um método Finalize. O coletor de lixo deve promover objetos propensos ao Finalize para gerações anteriores para facilitar a finalização, que os faz objetos de vida longa;
private string str1;
private string str2;
void DoSomeProcessing(?){
str1= GetResult(?);
str2= GetOtherResult(?);
}
void MakeDBCall(?){
PrepareForDBCall(str1,str2);
str1=null;
str2=null;
// Make a database (long running) call
}
}
Isso se aplica a todos os objetos que ainda estão estaticamente ou lexicalmente acessíveis, mas não são realmente necessários:
- Se você não precisar de mais uma variável estática em sua classe, ou alguma outra classe, configure-a como null;
- Se for possível, suprima o seu estado, esta também é uma boa ideia. Você pode ser capaz de eliminar mais de uma árvore antes de uma chamada de longa execução, por exemplo;
Não defina variáveis locais para null (C#) ou Nothing (Visual Basic NET.), pois o compilador JIT pode determinar estaticamente que a variável não é mais referenciada e não há necessidade de definí-la explicitamente como null.
Preste atenção também na alocações que ocorrem dentro de um loop, como as strings concatenadas usando o operador + =. Finalmente, os métodos de hashing e de comparação são, particularmente, lugares ruins para colocar alocações, pois são, muitas vezes, chamados repetidamente no contexto de pesquisa e classificação.
- Alocação de memória gerenciada é uma operação rápida e o coletor de lixo foi aperfeiçoado para alocações extremamente rápidas. A principal razão para a pré-alocação de memória em código não gerenciado é o de acelerar o processo de alocação. Isto não é um problema para o código gerenciado;
- Pré-alocando a memória, você faz mais alocações do que necessário o que pode desencadear as coletas de lixo desnecessárias;
- O coletor de lixo é incapaz de recuperar a memória que você recicla manualmente;
O coletor de lixo oferece mais dois métodos adicionais: Finalize e Dispose. Vamos discutir as diretrizes para esses métodos no próximo artigo desta série.
*****
O texto original está disponível em: http://blog.monitis.com/index.php/2012/04/10/improving-net-application-performance-part-4-garbage-collection/
http://imasters.com.br/artigo/24621/dotnet/melhorando-o-desempenho-do-aplicativo-net-parte-04
Carregando comentários...