PT | EN

Tech Trusted
Advisor

PT | EN

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.

 

Compartilhe este insight:

14 respostas

  1. 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. 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. 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. 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. 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. 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).

  5. 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. 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.

  7. 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)

Deixe uma resposta

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

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mais insights para o seu negócio

Veja mais alguns estudos e reflexões que podem gerar alguns insights para o seu negócio:

The example which motivated this post comes from the excellent book Designing Distributed Systems by Brendan Burns (co-founder of the...
Há muito valor em saber como fazer as coisas. Entretanto, antes, é preciso saber o porquê. Conheço excelentes profissionais, profundos...
Neste post, gostaria de compartilhar a estrutura que venho adotando em meus projetos com microsserviços. São algumas ideias que tenho...
What should be the execution result of the following code? using System; using System.Threading.Tasks; using static System.Console; using static System.IO.File;...
Software em funcionamento é mais relevante que documentação abrangente. Concordo com esse princípio expresso no manifesto ágil. Entretanto, acho que...
In this post, I would like to share my current reading list. If you are reading some of these books,...