Resolvendo Memory Leaks

Você tem “OutOfMemoryException” recorrente em um Serviço Windows. O que fazer?

Este é o primeiro post de uma série onde pretendo compartilhar, com considerável nível de detalhe, como resolver problemas de alocação de memória em aplicações .NET.

Meu desejo é fazer algo bem interativo. Em todos os posts vou:

  • propor algumas questões,
  • debater respostas nos comentários,
  • apresentar conceitos técnicos relevantes.

O nível de dificuldade dos temas será incremental.

Para começar, vamos examinar o seguinte cenário:

Você foi contratado, por uma grande instituição financeira, para resolver o problema de um Serviço Windows, escrito em .NET (não por você), que está disparando OutOfMemoryException com frequência. Você não tem autorização para fazer um dump completo da memória. Entretanto, você tem acesso aos fontes, ao “Performance Monitor” e ao “Gerenciador de Tarefas” da máquina onde esse serviço está rodando. Qual seria sua primeira medida? Por quê?

Conto com sua participação nos comentários. Vamos tratar de alguns conceitos fundamentais e começar a responder essa questão no próximo post.

 

Mais posts da série Resolvendo Memory Leaks

13 Comentários
  1. Alexsandro

    EU particularmente inicialmente gostaria muito de ver o problema acontecendo, eu iria abrir o Gerenciador de Tarefas e iria monitorar o uso de memoria do serviço ao longo do tempo.

    Apos isto eu iria ao código de fonte para entender como foi feito e como são usados os recursos do .net nesta aplicação para eu possa propor algumas alterações e depois medir os resultados novamente.

    1. Elemar Júnior

      Ótimo. Mas o que iria olhar no Task Manager, exatamente?

      1. Alexsandro

        Eu poderia usar o Performance Monitor para o que iria olhar no Task Manager, mas inicialmente eu iria monitorar manualmente os saltos de memoria acontecendo por algum tempo pra entender se o OutOfMemoryException acontece rapidamente, se caso demorar muito para ocorrer ai realmente iria usar o Performance Monitor para salvar o uso de memoria durante todo o ciclo da aplicação para uma analise.

        Eu sei que o Performance Monitor possui muitos sensores de instrumentação para o dotnet mas particularmente iria diretamente ao código a procura de como foi feita a manipulações com Strings, escopo de objetos e o uso recursos não gerenciáveis para propor uma melhoria que ajude o GC trabalhar melhor.

  2. Guatavo

    Eu iria ignorar o gerenciador de tarefas pq ele só diz a memória alocada e não em uso.

    Rodaria o perfmon e tiraria todos os marcadores e só marcaria o private bytes

    Se houver aumento é pq temos realmente um memory leak.
    Feito isso precisamos saber se é memory leak de memória gerenciada ou não.
    Para ajudar nisso colocaria no perfmon o bytes in all heap ( clr.net memory)
    Para definir qual está com problema a gente precisa levar em consideração o seguinte:
    Caso o private bytes aumentar e o heap ficar estável então o problema é na memória não gerenciada
    Caso o heap tenha um aumento constante então o problema é na memória gerenciada.

    Se o problema for nas memórias não gerenciadas provavelmente é algum dispose( de uma olhada nos io e com)

    1. Elemar Júnior

      Há, na aba detalhes, no Windows 10, a opção de exibir um conjunto rico de indicadores de memória (inclusive a memória commited).

      Será que é sempre a falta de um dispose?

  3. Gustavo

    Inicialmente eu iria ignorar o task manger e iria rodar o perfmon só com os counters de private bytes e bytes all in heap.

    Se o private bytes aumentar e o heap manter estável o problema é de memória não gerenciada. Normalmente é problema de disposing. Aí eu olharia (com e io)

  4. Thiago Borba

    O gerenciador de tarefas da um overview, onde será possível ver o consumo de memória aumentar e não diminuir, acompanhando de picos de processamento (possivelmente GC). No perfmon observo números do GC (número elevado e frequente de gen2, objetos promovidos para gen2 e principalmente pinned objects). Analiso também o número de contenção de threads, que pode estar relacionado ao leak (exemplo o compartilhamento de objeto de rede). Como código fontes em mãos faço uma pesquisa por alguns termos chaves, principalmente da system.IO, que normalmente vejo associado aos leaks (StreamReader, FileStream , etc). Em boa parte dos casos, esses recursos estão sendo utilizados sem um dispose correto e com isso causando fragmentação de memória, objetos pinados, etc).

    1. Elemar Júnior

      Sua principal suspeita é IO, se entendi bem.

  5. Flavio Peinado

    Se for possível gerar e implantar em produção uma versão gerando logs com o registo continuo de métodos e memória consumida em bytes. Esta é a matéria-prima para procurar por uma curva ascendente de uso de memória e os métodos invocados porque normalmente tem poços.

    Independente do acima eu procuraria por todas as conexões com banco e conexões socket de entrada e saída procurando por seus fechamentos (principalmente a falta deles).

    Também faria um cruzamento de versão de Windows e versão de Framework porque a versão 2.0 em especial é cravejada de bugs que geram memory leak.

  6. Marcos

    Alteraria o windows service adicionando vários comandos Trace.Writeline nos principais fluxos. Depois utilizaria a ferramenta de Debug View na máquina de produção. Assim poderia saber qual função está ocasionando a falha.

    1. Elemar Júnior

      Como você saberia qual função está causando o problema?

  7. Nicolas Tarzia

    Antes de mais nada é super importante conversar com as pessoas responsáveis para entender com maiores detalhes, o horário/frequencia que ocorre o erro, conhecer maiores detalhes da aplicação, muitas vezes a rotina que está lançando OutOfMemory é a própria que precisa ser corrigida, após esse bate papo, podemos conectar no servidor e analisar os serviços instalados (possivelmente OutOfMemory pode ser causado por outra app) e a primeira ação no servidor seria visualizar o event viewer (caso esteja gravando as acoes) e verificar se está ocorrendo exceptions não tratadas na aplicação que não esteja despejando as variáveis da memória. (Se não podemos gerar os dumps através do próximo, imagino também que não é possível rodar o procmon, isso ajudaria bastante também) se o erro ocorre com frequência podemos visualizar a alocação de memória dentro do “performance monitor”, se não estiver diminuindo a alocação de memória provavelmente o erro está na imementacao do próprio projeto.
    Paralelo a isso , executaria o code analysis dentro do código e estudaria a implementação dele, procurando algum erro de implementação, onde está alocando os objetos da memória e não estão eliminando (porque o GC entende que são utilizados)

    1. Elemar Júnior

      Gostei da abordagem de conversar com as pessoas. Em tempo, Dumps não são autorizados.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *