Vazamento de memory em c #

É possível, em um sistema gerenciado, vazar memory quando você se certifica de que todas as alças, coisas que implementam o IDispose são descartadas?

Haveria casos em que algumas variables ​​são deixadas de fora?

Os manipuladores de events são uma fonte muito comum de vazamentos de memory não óbvios. Se você assinar um evento em object1 do object2, faça object2.Dispose () e finja que ele não existe (e retire todas as referências do seu código), há uma referência implícita no evento do object1 que evitará que object2 seja lixo coletado.

 MyType object2 = new MyType(); // ... object1.SomeEvent += object2.myEventHandler; // ... // Should call this // object1.SomeEvent -= object2.myEventHandler; object2.Dispose(); 

Este é um caso comum de vazamento – esquecendo-se de cancelar a inscrição facilmente dos events. Obviamente, se object1 for coletado, object2 também será coletado, mas não até então.

Eu não acho que vazamentos de memory estilo C ++ são possíveis. O coletor de lixo deve contabilizar isso. É possível criar um object estático que agrega referências a objects, mesmo que os objects nunca sejam usados ​​novamente. Algo assim:

 public static class SomethingFactory { private static List listOfSomethings = new List(); public static Something CreateSomething() { var something = new Something(); listOfSomethings.Add(something); return something; } } 

Esse é um exemplo obviamente estúpido, mas seria o equivalente a um memory leaks de tempo de execução gerenciado.

Como outros apontaram, desde que não haja um bug real no gerenciador de memory, as classs que não usam resources não gerenciados não vazarão memory.

O que você vê no .NET não são vazamentos de memory, mas objects que nunca são descartados. Um object não será descartado desde que o coletor de lixo possa encontrá-lo no gráfico do object. Então, se qualquer object vivo em qualquer lugar tiver uma referência ao object, ele não será descartado.

O registro de events é uma boa maneira de fazer isso acontecer. Se um object se registra em um evento, o que ele registrou tem uma referência a ele, e mesmo que você elimine todas as outras referências ao object, até que ele cancele o registro (ou o object registrado se torne inacessível) ele permanecerá ativo.

Portanto, você precisa estar atento a objects que se registram em events estáticos sem o seu conhecimento. Um recurso interessante do ToolStrip , por exemplo, é que, se você alterar o tema de exibição, ele será exibido automaticamente no novo tema. Ele realiza esse recurso bacana registrando o evento SystemEvents.UserPreferenceChanged estático. Quando você altera o tema do Windows, o evento é gerado e todos os objects ToolStrip que estão ouvindo o evento são notificados de que há um novo tema.

Ok, então suponha que você decida jogar fora um ToolStrip no seu formulário:

 private void DiscardMyToolstrip() { Controls.Remove("MyToolStrip"); } 

Você agora tem um ToolStrip que nunca morrerá. Mesmo que não esteja mais em seu formulário, toda vez que o usuário alterar os temas, o Windows informará devidamente a ToolStrip outra forma seria inexistente. Toda vez que o coletor de lixo é executado, ele pensa “não consigo jogar fora esse object, o evento UserPreferenceChanged está usando-o”.

Isso não é um memory leaks. Mas bem poderia ser.

Coisas como esta fazem um profiler de memory inestimável. Execute um profiler de memory, e você vai dizer “isso é estranho, parece que há dez mil objects ToolStrip na pilha, mesmo que haja apenas um no meu formulário. Como isso aconteceu?”

Ah, e caso você esteja se perguntando por que algumas pessoas acham que os setters de propriedade são maus: para obter um ToolStrip para cancelar o registro do evento UserPreferenceChanged , defina sua propriedade Visible como false .

Os delegates podem resultar em vazamentos de memory não intuitivos.

Sempre que você cria um delegado de um método de instância, uma referência a essa instância é armazenada “nesse” delegado.

Além disso, se você combinar vários delegates em um delegado de multidifusão, você terá um grande blob de referências a vários objects que serão excluídos da garbage collection, desde que esse delegado de multidifusão esteja sendo usado em algum lugar.

Se você estiver desenvolvendo um aplicativo WinForms, um “vazamento” sutil é a propriedade Control.AllowDrop (usada para ativar o recurso arrastar e soltar). Se AllowDrop for definido como “true”, o CLR ainda manterá seu controle através de um System.Windows.Forms.DropTarget . Para corrigir isso, certifique-se de que a propriedade AllowDrop seu Control esteja definida como false quando você não precisar mais dela, e o CLR cuidará do resto.

Como já mencionado, as referências mantêm o uso da memory ao longo do tempo. Uma maneira fácil de entrar nessa situação é com events. Se você tiver um object de longa vida com algum evento que seus outros objects escutem, se os ouvintes nunca forem removidos, o evento no object de longa duração manterá essas outras instâncias ativas por muito tempo depois que elas não forem mais necessárias.

A única razão para o memory leaks no aplicativo .NET é que os objects ainda estão sendo referenciados, embora sua vida útil tenha terminado. Portanto, o coletor de lixo não pode coletá-los. E eles se tornam objects de longa duração.

Eu acho que é muito fácil causar vazamento, assinando events sem cancelá-los quando a vida do object terminar.

Você pode achar meu novo artigo útil: Como detectar e evitar vazamentos de memory e resources em aplicativos .NET

O Reflection emit é outra fonte potencial de vazamentos, como, por exemplo, desserializadores de objects embutidos e clientes SOAP / XML sofisticados. Pelo menos em versões anteriores da estrutura, o código gerado em AppDomains dependentes nunca foi descarregado …

É um mito que você não pode vazar memory no código gerenciado. Concedido, é muito mais difícil do que em C ++ não gerenciado, mas há um milhão de maneiras de fazer isso. Objetos estáticos contendo referências, referências desnecessárias, cache, etc. Se você estiver fazendo as coisas da maneira certa, muitos de seus objects não serão coletados até muito mais tarde do que o necessário, o que é um memory leaks também na minha opinião, de uma forma prática e não teórica.

Felizmente, existem ferramentas que podem ajudá-lo. Eu uso muito o CLR Profiler da Microsoft – não é a ferramenta mais fácil de usar já escrita, mas é definitivamente muito útil e gratuita.

Depois que todas as referências a um object tiverem desaparecido, o coletor de lixo liberará esse object na próxima passagem. Eu não diria que é impossível vazar memory, mas é bastante difícil, a fim de vazar você teria que ter uma referência a um object sentado sem perceber.

Por exemplo, se você instanciar objects em uma lista e depois esquecer de removê-los da lista quando terminar, esqueça de descartá-los.

É possível haver vazamentos se resources não gerenciados não forem limpos adequadamente. Classes que implementam IDisposable podem vazar.

No entanto, as referências regulares a objects não exigem gerenciamento explícito de memory da mesma forma que as linguagens de nível inferior.

Não é realmente um memory leaks, mas é muito fácil ficar sem memory ao usar objects grandes (maiores que 64K se bem me lembro). Eles são armazenados no LOH e não são desfragmentados. Portanto, usar esses objects grandes e liberá-los libera a memory no LOH, mas essa memory livre não é mais usada pelo tempo de execução do .NET para esse processo. Assim, você pode facilmente ficar sem espaço no LOH usando apenas alguns objects grandes no LOH. Esse problema é conhecido pela Microsoft, mas, como lembro agora, a solução para isso está sendo planejada.

Os únicos vazamentos (além de erros no tempo de execução que podem estar presentes, embora não sejam muito prováveis ​​devido à garbage collection) serão para resources nativos. Se você P / Invoke em uma biblioteca nativa que abre identificadores de arquivo, ou conexões de soquete, ou o que for em nome do seu aplicativo gerenciado, e nunca os fecha explicitamente (e não os manipula em um triturador ou destruidor / finalizador), você pode ter memory ou vazamentos de resources porque o tempo de execução não pode gerenciar todos esses automaticamente para você.

Se você ficar com resources puramente gerenciados, você deve estar bem. Se você tiver qualquer tipo de memory leaks sem chamar o código nativo, isso é um erro.

Embora seja possível que algo no framework tenha um vazamento, é mais provável que você tenha algo que não esteja sendo descartado adequadamente ou que algo esteja impedindo o GC de descartá-lo, o IIS seria um excelente candidato para isso.

Basta lembrar que nem tudo no .NET é totalmente gerenciado código, COM interop, arquivo io como streams de arquivos, solicitações de database, imagens, etc.

Um problema que tivemos há pouco tempo (.net 2.0 no IIS 6) foi que nós criamos uma imagem e a descartamos, mas o IIS não liberava a memory por um tempo.

No meu último trabalho, estávamos usando uma biblioteca .NET SQLite de terceiros que vazava como uma peneira.

Estávamos fazendo muitas inserções rápidas de dados em uma situação estranha em que a conexão com o database precisava ser aberta e fechada a cada vez. A biblioteca de terceiros fez parte da mesma abertura de conexão que deveríamos fazer manualmente e não documentou. Ele também manteve as referências em algum lugar que nunca encontramos. O resultado foi 2x como muitas conexões sendo abertas como deveriam ser e apenas 1/2 sendo fechadas. E desde que as referências foram realizadas, tivemos um memory leaks.

Obviamente, isso não é o mesmo que um memory leaks C / C ++ clássico, mas para todos os efeitos, era um para nós.

Se for considerado memory leaks, também pode ser obtido com esse tipo de código:

 public class A { B b; public A(B b) { this.b = b; } ~A() { b = new B(); } } public class B { A a; public B() { this.a = new A(this); } ~B() { a = new A(this); } } class Program { static void Main(string[] args) { { B[] toBeLost = new B[100000000]; foreach (var c in toBeLost) { toBeLost.ToString(); //to make JIT compiler run the instantiation above } } Console.ReadLine(); } } 

Pequenas funções ajudam a evitar “vazamentos de memory”. Porque coletor de lixo libera variables ​​locais no final das funções. Se a function for grande e exigir muita memory, você terá que liberar variables ​​locais que consomem muita memory e não são mais necessárias. Variáveis ​​globais similares (arrays, listas) também são ruins.

Eu experimentei vazamentos de memory em C # ao criar imagens e não descartá-las. Que é um pouco estranho. As pessoas dizem que você tem que chamar .Dispose () em todos os objects que o possuem. Mas a documentação para funções gráficas C # nem sempre menciona isso, por exemplo, para a function GetThumbnailImage (). Compilador C # deve avisá-lo sobre isso, eu acho.

Um lembrete auto Como encontrar memory leaks:

  • Remover e gc.collect chamadas.
  • Espere até termos certeza de que a memory está vazando.
  • Crie um arquivo de despejo do gerenciador de tarefas.
  • Abra os arquivos de despejo usando DebugDiag .
  • Analise o resultado primeiro. O resultado de lá deve nos ajudar, o que normalmente leva a maior parte da memory.
  • Corrigir o código até que não haja memory leaks.
  • Use aplicativos de terceiros, como o profiler .net. (Nós podemos usar julgamento, mas precisa resolver o problema o mais rápido possível. O primeiro despejo deve nos ajudar principalmente sobre como o vazamento)
  • Se o problema estiver na memory virtual, é necessário observar a memory não gerenciada. (Geralmente há outra configuração lá que precisa ser ativada)
  • Execute aplicativos de terceiros com base em como eles são usados.

Problema de memory leaks comum:

  • Eventos / delegates nunca são removidos. (Ao descartar, certifique-se de que o evento não está registrado) – veja a resposta da ReepChopsey
  • List / Dictionary nunca foram apagados.
  • Objeto referenciado a outro object que é salvo na memory nunca será descartado. (Clone para facilitar o gerenciamento)