Pergunta

Quais são todas as maneiras possíveis pelas quais podemos obter vazamentos de memória no .NET?

Eu conheço dois:

  1. Não cancelando o registro corretamente Manipuladores/delegados de eventos.
  2. Não descartando controles filho dinâmicos no Windows Forms:

Exemplo:

// Causes Leaks  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// Correct Code  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

Atualizar:A ideia é listar armadilhas comuns que não são muito óbvias (como as acima).Normalmente, a noção é que vazamentos de memória não são um grande problema por causa do coletor de lixo.Não como costumava ser em C++.


Ótima discussão pessoal, mas deixe-me esclarecer ...por definição, se não houver nenhuma referência a um objeto no .NET, ele será coletado como lixo em algum momento.Portanto, essa não é uma forma de induzir vazamentos de memória.

No ambiente gerenciado, eu consideraria um vazamento de memória se você tivesse uma referência não intencional a qualquer objeto do qual não tem conhecimento (daí os dois exemplos na minha pergunta).

Então, quais são as várias maneiras possíveis pelas quais esse vazamento de memória pode acontecer?

Foi útil?

Solução

Bloqueie o thread do finalizador.Nenhum outro objeto será coletado como lixo até que o thread do finalizador seja desbloqueado.Assim, a quantidade de memória usada aumentará cada vez mais.

Leitura adicional: http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/

Outras dicas

Isso realmente não causa vazamentos, apenas dá mais trabalho para o GC:

// slows GC
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// better  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

// best
using( Label label = new Label() )
{ 
    this.Controls.Add(label);  
    this.Controls.Remove(label);  
}

Deixar componentes descartáveis ​​assim nunca é um grande problema em um ambiente gerenciado como o .Net - isso é uma grande parte do que significa gerenciado.

Você certamente tornará seu aplicativo mais lento.Mas você não vai deixar bagunça para mais nada.

Configurando o GridControl.DataSource propriedade diretamente sem usar uma instância da classe BindingSource (http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx).

Isso causou vazamentos em meu aplicativo que demorei um pouco para serem rastreados com um criador de perfil. Eventualmente, encontrei este relatório de bug ao qual a Microsoft respondeu: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260

É engraçado que na documentação da classe BindingSource a Microsoft tente fazê-la passar por uma classe legítima e bem pensada, mas acho que eles apenas a criaram para resolver um vazamento fundamental em relação aos gerenciadores de moeda e à vinculação de dados aos controles de grade.

Cuidado com este, aposto que há muitos aplicativos com vazamento por aí por causa disso!

Não há como fornecer uma lista abrangente ...isso é muito parecido com perguntar "Como você pode se molhar?"

Dito isso, certifique-se de chamar Dispose() em tudo que implementa IDisposable e implemente IDisposable em qualquer tipo que consuma recursos não gerenciados de qualquer tipo.

De vez em quando, execute algo como FxCop em sua base de código para ajudá-lo a aplicar essa regra - você ficaria surpreso com a profundidade de alguns objetos descartáveis ​​que ficam enterrados em uma estrutura de aplicativo.

Exceções nos métodos Finalize (ou Dispose de um Finaliser) que impedem que recursos não gerenciados sejam descartados corretamente.Um problema comum é devido ao programador assumindo quais objetos de ordem serão descartados e tentando liberar objetos pares que já foram descartados, resultando em uma exceção e o restante do método Finalise/Dispose from Finalize não sendo chamado.

Tenho 4 itens adicionais a acrescentar a esta discussão:

  1. O encerramento de threads (Thread.Abort()) que criaram controles de UI sem se preparar adequadamente para tal evento pode fazer com que a memória seja usada com expectativa.

  2. Acessar recursos não gerenciados por meio do Pinvoke e não limpá-los pode causar vazamentos de memória.

  3. Modificando objetos de string grandes.Não necessariamente um vazamento de memória, uma vez fora do escopo, o GC cuidará disso, no entanto, em termos de desempenho, seu sistema poderá sofrer um impacto se strings grandes forem modificadas com frequência, porque você não pode realmente depender do GC para garantir que a pegada do seu programa seja mínimo.

  4. Criação frequente de objetos GDI para realizar desenhos personalizados.Se estiver executando trabalho GDI com frequência, reutilize um único objeto GDI.

Chamar IDisposable sempre é o lugar mais fácil para começar e, definitivamente, uma maneira eficaz de obter todos os frutos de vazamento de memória mais fáceis de encontrar na base de código.No entanto, nem sempre é suficiente.Por exemplo, também é importante entender como e quando o código gerenciado é gerado em tempo de execução e que, depois que os assemblies são carregados no domínio do aplicativo, eles nunca são descarregados, o que pode aumentar o espaço ocupado pelo aplicativo.

Para evitar vazamentos de memória .NET:

1) Empregue a construção 'using' (ou construção 'try-finally) sempre que um objeto com interface 'IDisposable' for criado.

2) Torne as classes 'IDisposable' se elas criarem um thread ou adicionarem um objeto a uma coleção estática ou de longa duração.Lembre-se de que um 'evento' C# é uma coleção.

Aqui está um pequeno artigo sobre Dicas para evitar vazamentos de memória.

Você está falando sobre uso inesperado de memória ou vazamentos reais?Os dois casos listados não são exatamente vazamentos;são casos em que os objetos permanecem por mais tempo do que o pretendido.

Em outras palavras, são referências que quem os chama de vazamentos de memória não conhecia ou esqueceu.

Editar:Ou são bugs reais no coletor de lixo ou no código não gerenciado.

Editar 2:Outra maneira de pensar sobre isso é sempre garantir que as referências externas aos seus objetos sejam liberadas de forma adequada.Externo significa código fora do seu controle.Qualquer caso em que isso aconteça é um caso em que você pode "vazar" memória.

  1. Manter referências a objetos que você não precisa mais.

Com relação a outros comentários - uma maneira de garantir que Dispose seja chamado é usar using...quando a estrutura do código permite.

Uma coisa que foi realmente inesperada para mim é esta:

Region oldClip = graphics.Clip;
using (Region newClip = new Region(...))
{
    graphics.Clip = newClip;
    // draw something
    graphics.Clip = oldClip;
}

Onde está o vazamento de memória?Certo, você deveria ter descartado oldClip, também!Porque Graphics.Clip é uma das raras propriedades que retorna um novo objeto descartável toda vez que o getter é invocado.

Tess Fernandez tem ótimas postagens no blog sobre como encontrar e depurar vazamentos de memória.Laboratório 6 Laboratório 7

Muitas das coisas que podem causar vazamentos de memória em linguagens não gerenciadas ainda podem causar vazamentos de memória em linguagens gerenciadas.Por exemplo, políticas de cache ruins pode resultar em vazamentos de memória.

Mas, como Greg e Danny disseram, não existe uma lista abrangente.Qualquer coisa que possa resultar na retenção da memória após sua vida útil pode causar vazamento.

Threads em deadlock nunca liberarão raízes.Obviamente, você poderia argumentar que o impasse apresenta um problema maior.

Um encadeamento finalizador em conflito impedirá a execução de todos os finalizadores restantes e, portanto, impedirá que todos os objetos finalizáveis ​​sejam recuperados (já que eles ainda estão sendo enraizados pela lista alcançável).

Em uma máquina com várias CPU, você pode criar objetos finalizáveis ​​mais rápido do que o thread do finalizador pode executar finalizadores.Enquanto isso for sustentado, você “vazará” memória.Provavelmente não é muito provável que isso aconteça na natureza, mas é fácil de reproduzir.

O heap de objetos grandes não está compactado, portanto você pode vazar memória por meio de fragmentação.

Existem vários objetos que devem ser liberados manualmente.Por exemplo.objetos remotos sem locação e assemblies (deve descarregar AppDomain).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top