Quais estratégias e ferramentas são úteis para encontrar vazamentos de memória no .NET?

152

Eu escrevi C ++ por 10 anos. Encontrei problemas de memória, mas eles poderiam ser corrigidos com uma quantidade razoável de esforço.

Nos últimos dois anos, tenho escrito C #. Acho que ainda tenho muitos problemas de memória. Eles são difíceis de diagnosticar e corrigir devido à não determinação, e porque a filosofia do C # é que você não precisa se preocupar com essas coisas quando definitivamente faz isso.

Um problema em particular que encontro é que tenho que dispor e limpar explicitamente tudo no código. Se não o fizer, os criadores de perfil de memória não ajudarão realmente, porque há tanta palha flutuando sobre você que não é possível encontrar um vazamento em todos os dados que eles estão tentando mostrar. Gostaria de saber se tenho a ideia errada ou se a ferramenta que tenho não é a melhor.

Que tipo de estratégias e ferramentas são úteis para combater vazamentos de memória no .NET?

Scott Langham
fonte
O título da sua postagem realmente não corresponde à pergunta da sua postagem. Eu sugiro que você atualize seu título.
Kevin
Você está certo. Desculpe, eu estava ficando um pouco cansado do vazamento atual que estou caçando! Título atualizado.
Scott Langham
3
@ Scott: Não fique farto do .NET, não é esse o problema. O seu código é
GEOCHET 25/09/08
3
Sim, meu código ou as bibliotecas de terceiros que tenho o prazer de usar.
Scott Langham
@ Scott: Veja minha resposta. MemProfiler vale a pena. A utilização também fornecerá um novo nível de entendimento do mundo do .NET GC.
GEOCHET

Respostas:

51

Uso o MemProfiler da Scitech quando suspeito de um vazamento de memória.

Até agora, achei muito confiável e poderoso. Ele salvou meu bacon em pelo menos uma ocasião.

O GC funciona muito bem no .NET IMO, mas, como qualquer outro idioma ou plataforma, se você escreve um código incorreto, coisas ruins acontecem.

GEOCHET
fonte
3
Sim, eu tive uma chance com este, e isso me ajudou a chegar ao fundo de alguns vazamentos complicados. Os maiores vazamentos que eu provei foram causados ​​por bibliotecas de terceiros em código não gerenciado que eles acessaram via interoperabilidade. Fiquei impressionado que essa ferramenta detectou vazamentos no código não gerenciado e no código gerenciado.
9118 Scott Langham
1
Aceitei isso como resposta porque é o que funcionou para mim no final, mas acho que todas as outras respostas são muito úteis. A propósito, essa ferramenta é mais comumente chamada de Mem Profiler da SciTech!
9138 Scott Langham
41

Apenas para o problema de esquecer o descarte, tente a solução descrita nesta postagem do blog . Aqui está a essência:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif
Jay Bazuzi
fonte
Eu preferiria para lançar uma exceção em vez de Debug.Fail
Pedro77
17

Usamos o software Ants Profiler Pro da Red Gate em nosso projeto. Funciona muito bem para todos os aplicativos baseados em linguagem .NET.

Descobrimos que o .NET Garbage Collector é muito "seguro" na limpeza de objetos na memória (como deveria ser). Ele manteria os objetos por perto apenas porque poderíamos usá-lo em algum momento no futuro. Isso significava que precisamos ter mais cuidado com o número de objetos que inflamos na memória. No final, convertemos todos os nossos objetos de dados em um "inflar sob demanda" (pouco antes de um campo ser solicitado) para reduzir a sobrecarga de memória e aumentar o desempenho.

Edição: Aqui está uma explicação adicional do que quero dizer com "inflar sob demanda". Em nosso modelo de objeto de nosso banco de dados, usamos Propriedades de um objeto pai para expor o (s) objeto (s) filho (s). Por exemplo, se tivéssemos algum registro que referenciasse outro registro de "detalhes" ou "pesquisa" individualmente, a estrutura seria assim:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

Descobrimos que o sistema acima criou alguns problemas reais de memória e desempenho quando havia muitos registros na memória. Então, mudamos para um sistema em que os objetos eram inflados apenas quando solicitados e as chamadas ao banco de dados eram feitas apenas quando necessário:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Isso se mostrou muito mais eficiente, porque os objetos foram mantidos sem memória até serem necessários (o método Get foi acessado). Ele proporcionou um aumento de desempenho muito grande na limitação de acessos ao banco de dados e um enorme ganho em espaço na memória.

Marca
fonte
Eu segundo este produto. Foi um dos melhores criadores de perfil que já usei.
GORD
Achei o profiler muito bom para analisar problemas de desempenho. No entanto, as ferramentas de análise de memória eram muito ruins. Encontrei um vazamento com esta ferramenta, mas foi um lixo ajudar-me a identificar a causa do vazamento. E isso não ajuda em nada se o vazamento estiver no código não gerenciado.
Scott Langham
Ok, a nova versão 5.1, é muito melhor. É melhor ajudá-lo a encontrar a causa do vazamento (embora - ainda existam alguns problemas que o ANTS me disse que corrigirá na próxima versão). No entanto, o código ainda não é gerenciado, mas se você não se preocupa com o código não gerenciado, agora é uma ferramenta muito boa.
Scott Langham
7

Você ainda precisa se preocupar com memória ao escrever código gerenciado, a menos que seu aplicativo seja trivial. Sugerirei duas coisas: primeiro, leia o CLR via C # porque ele ajudará você a entender o gerenciamento de memória no .NET. Segundo, aprenda a usar uma ferramenta como CLRProfiler (Microsoft). Isso pode lhe dar uma idéia do que está causando seu vazamento de memória (por exemplo, você pode dar uma olhada na fragmentação de heap de objetos grandes)

Zac Gochenour
fonte
Sim. O CLRPRofiler é bem legal. Pode ficar um pouco explosivo com as informações ao tentar explorar a exibição que você fornece dos objetos alocados, mas está tudo lá. É definitivamente um bom ponto de partida, especialmente como gratuito.
Scott Langham
6

Você está usando código não gerenciado? Se você não estiver usando código não gerenciado, de acordo com a Microsoft, vazamentos de memória no sentido tradicional não serão possíveis.

A memória usada por um aplicativo pode não ser liberada, portanto, a alocação de memória de um aplicativo pode aumentar ao longo da vida útil do aplicativo.

De Como identificar vazamentos de memória no Common Language Runtime no Microsoft.com

Um vazamento de memória pode ocorrer em um aplicativo .NET Framework quando você usa código não gerenciado como parte do aplicativo. Esse código não gerenciado pode vazar memória e o tempo de execução do .NET Framework não pode solucionar esse problema.

Além disso, um projeto pode parecer apenas ter um vazamento de memória. Essa condição pode ocorrer se muitos objetos grandes (como objetos DataTable) forem declarados e adicionados a uma coleção (como um DataSet). Os recursos que esses objetos possuem podem nunca ser liberados e os recursos permanecem ativos por toda a execução do programa. Isso parece ser um vazamento, mas na verdade é apenas um sintoma da maneira como a memória está sendo alocada no programa.

Para lidar com esse tipo de problema, você pode implementar o IDisposable . Se você quiser ver algumas das estratégias para lidar com o gerenciamento de memória, sugiro procurar por IDisposable, XNA, gerenciamento de memória, pois os desenvolvedores de jogos precisam ter uma coleta de lixo mais previsível e, portanto, devem forçar o GC a fazer suas coisas.

Um erro comum é não remover manipuladores de eventos que se inscrevem em um objeto. Uma assinatura do manipulador de eventos impedirá que um objeto seja reciclado. Além disso, dê uma olhada na instrução using , que permite criar um escopo limitado para a vida útil de um recurso.

Timothy Lee Russell
fonte
5
Consulte blogs.msdn.com/tess/archive/2006/01/23/… . Realmente não importa se o vazamento de memória é "tradicional" ou não, ainda é um vazamento.
Constantin
2
Entendo o seu argumento - mas a alocação e reutilização ineficientes da memória por um programa são diferentes de um vazamento de memória.
Timothy Lee Russell
boa resposta, obrigado por lembrar que os manipuladores de eventos podem ser perigosos.
frameworkninja
3
@ Timothy Lee Russel: Se uma quantidade ilimitada de memória (1) pode permanecer alocada simultaneamente (enraizada) após se tornar inútil (2), sem que nada no sistema tenha as informações e o ímpeto necessários para desenraizá-la em tempo hábil, isso é um vazamento de memória . Mesmo que a memória possa ser liberada algum dia, se houver coisas inúteis suficientes para acumular o sistema antes que isso aconteça, é um vazamento. (1) Maior que O (N), sendo N a quantidade de alocação útil; (2) O material é inútil se a remoção de referências a ele não afetar a funcionalidade do programa.
Supercat
2
@ Timothy Lee Russel: O padrão normal de "vazamento de memória" ocorre quando a memória é mantida por uma entidade em nome de outra entidade , esperando ser informada quando não for mais necessária, mas a última abandona a entidade sem informar a primeira. A entidade que detém a memória realmente não precisa, mas não há como determinar isso.
supercat
5

Este blog tem algumas orientações realmente maravilhosas usando o windbg e outras ferramentas para rastrear vazamentos de memória de todos os tipos. Excelente leitura para desenvolver suas habilidades.

twk
fonte
5

Acabei de ter um vazamento de memória em um serviço do Windows que eu consertei.

Primeiro, tentei o MemProfiler . Achei muito difícil de usar e nada amigável.

Em seguida, usei o JustTrace, que é mais fácil de usar e fornece mais detalhes sobre os objetos que não são descartados corretamente.

Isso me permitiu resolver o vazamento de memória com muita facilidade.

billybob
fonte
3

Se os vazamentos que você está observando são devidos a uma implementação de cache em fuga, este é um cenário em que você pode considerar o uso de WeakReference. Isso pode ajudar a garantir que a memória seja liberada quando necessário.

No entanto, no IMHO, seria melhor considerar uma solução sob medida - apenas você realmente sabe quanto tempo precisa para manter os objetos por perto; portanto, projetar o código de manutenção adequado para sua situação geralmente é a melhor abordagem.

Chris Ballard
fonte
3

Prefiro dotmemory de Jetbrains

josepainumkal
fonte
você pode ser o único :)
HellBaby
Eu também tentei. Eu acho que essa é uma boa ferramenta. Fácil de usar, informativo. Integra-se ao Visual Studio
redeye
No nosso caso, ao solucionar problemas de vazamento de memória, a ferramenta Instantâneo do Visual Studio travou / não ocorreu um instantâneo. O Dotmemory manteve seus cool e manipulou vários instantâneos de mais de 3 GB com (aparentemente) facilidade.
Michael Kargl 27/09/19
3

Big guns - Ferramentas de depuração para Windows

Esta é uma incrível coleção de ferramentas. Você pode analisar montes gerenciados e não gerenciados com ele e pode fazê-lo offline. Isso foi muito útil para depurar um de nossos aplicativos ASP.NET que continuava reciclando devido ao uso excessivo de memória. Eu só tive que criar um despejo de memória completo do processo em execução no servidor de produção, todas as análises foram feitas offline no WinDbg. (Verificou-se que alguns desenvolvedores estavam usando demais o armazenamento de sessões na memória.)

O blog "Se quebrado está ..." tem artigos muito úteis sobre o assunto.

Constantin
fonte
2

A melhor coisa a ter em mente é acompanhar as referências aos seus objetos. É muito fácil acabar com referências suspensas a objetos com os quais você não se importa mais. Se você não vai mais usar algo, livre-se dele.

Acostume-se a usar um provedor de cache com vencimentos deslizantes, para que, se algo não for referenciado por uma janela de tempo desejada, ele seja desreferenciado e limpo. Mas se estiver sendo acessado muito, dirá na memória.

Gord
fonte
2

Uma das melhores ferramentas é usar o Debugging Tools for Windows e fazer um despejo de memória do processo usando o adplus ; em seguida, use o windbg e o plugin sos para analisar a memória do processo, os threads e as pilhas de chamadas.

Você também pode usar esse método para identificar problemas nos servidores, depois de instalar as ferramentas, compartilhar o diretório e conectar-se ao compartilhamento pelo servidor usando (net use) e fazer uma pane ou travar o processo.

Então analise offline.

Stuart McConnell
fonte
Sim, isso funciona bem, especialmente para itens mais avançados ou para diagnosticar problemas no software lançado ao qual você não pode conectar facilmente um depurador. Este blog tem muitas dicas para usar bem essas ferramentas: blogs.msdn.com/tess
Scott Langham
2

Após uma das minhas correções para o aplicativo gerenciado, tive a mesma coisa, como verificar se meu aplicativo não terá o mesmo vazamento de memória após a minha próxima alteração, por isso escrevi algo como a estrutura de Verificação de Liberação de Objeto. o pacote NuGet ObjectReleaseVerification . Você pode encontrar uma amostra aqui https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample e informações sobre essa amostra http://outcoldman.ru/en/blog/show/322

outcoldman
fonte
0

No Visual Studio 2015, considere usar a ferramenta de diagnóstico pronta para uso da memória para coletar e analisar dados de uso da memória.

A ferramenta Uso da memória permite tirar um ou mais instantâneos do heap de memória gerenciada e nativa para ajudar a entender o impacto no uso de memória dos tipos de objeto.

Michael Freidgeim
fonte
0

uma das melhores ferramentas que usei no DotMemory. você pode usar essa ferramenta como uma extensão do VS. Depois de executar o aplicativo, você pode analisar todas as partes da memória (por Objeto, NameSpace, etc.) que o aplicativo usa e tirar algumas fotos dele Compare-o com outros SnapShots. DotMemory

Rebwar
fonte