Arquitetura em prática

por Fabio Margarito Martins de Barros

O perigo da concatenação de strings

Trabalho em uma grande instituição financeira e esta semana tivemos um problema sério de alto consumo de memória de um único processo, em um servidor de aplicação extremamente acessado. O consumo exagerado era de um processo de Cache, ele estava com quase 1GB de memória alocada.Bom, sendo um processo de Cache isto pode ser uma verdade em muitas situações, entretanto, a informação armazenada em Cache no momento, era de apenas 20 MB, portanto, 1GB alocado é algo fora dos padrões.

Descobrir memory leak não é uma tarefa muito fácil, realizar um Code Review pode ser uma solução, pois podemos percorrer o código e procurar por causas que podem determinar esta condição. Mas há um porem, se a aplicação for muito grande, isto pode demorar muito,e demorar a encontrar um problema de produção é muito grave para nosso setor de atuação.Quando há qualquer tipo de problema em produção, preferimos realizar análise de Dump por ser mais rápido.

O Dump do processo é gerado pelo pessoal de produção que nos encaminha para análise. Análise de Dump é uma técnica de debug onde analisamos a memória de um processo. No fim deste post, deixo algumas referencias para quem gosta de desafios.

Vamos lá, para realizar a análise vamos utilizar o programa WinDbg:

Pela opção File/Open Crache Dump, vamos carregar o Dump.  

 Agora vou carregar a DLL SOS.dll, esta DLL fornece comandos para manipular Dump de código gerenciado. Para cada versão do .Net Framework, há uma versão diferente. No caso, estou carregando a Dll do framework 2.0

Como é um memory leak, vou direto ao comando !dumpheap -stat. O objetivo deste comando é trazer estatísticas de consumo de memória por tipo. O resultado é expresso em bytes. Dependendo do tamanho do dump, o resultado poderá demorar um pouco. Por motivos de confidencialidade, estou ocultando algumas informações.

Achamos os vilões. Percebam que o tipo System.String ocupa 560 MB e temos em torno de 400 MB com objetos prontos para serem liberados. Somando, temos quase os 1GB de memória consumida. É um tanto estranho o tipo System.String ocupar tanto espaço não?Agora vamos recuperar todos os 281 mil objetos do tipo string. Para esta tarefa vamos usar o comando !dumpheap -mt 793308ec.

 Como são milhares de objetos, vamos pesquisar por amostragem. A idéia é buscar a raiz de cada objeto. Para realizar esta busca, vamos utilizar o comando !gcroot <endereço do objeto>, no caso !gcroot 3dc31000.

Temos um suspeito agora, o CacheCleanup. Varrendo outros objetos percebi que todos têm a mesma raiz, ou seja, provavelmente o problema está lá. Abaixo um trecho do código danoso: 

#region Access Log			
Handler.LogHandler.Tracking("Access Method: " + this.GetType().ToString() + "->" + ((object)MethodBase.GetCurrentMethod()).ToString() + " ;");
#endregion Access Log
Como podemos ver, o código está cheio de concatenações em um trecho crítico do projeto que trata traces, e este é chamado milhares de vezes. Concatenação de string com o operador + não é uma boa prática devido à maneira que ocorre a manipulação de memória. Mais informações você pode encontrar neste site. Para corrigir, apenas substituí por String.Builder e o problema foi resolvido.

Links

Blog da Tess Fernandes tem tudo sobre debug

Debugging Tools for Windows

Conclusão

O que pode ser algo inofensivo, em cenários onde há milhões de chamadas em curto espaço de tempo pode se tornar uma dor de cabeça sem precedentes e custar muito $$$$$.

[]’s

Fabio Margarito  

Posted: Jul 23 2009, 14:51 by fabiomargarito | Comments (7) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under:
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); var pageTracker = _gat._getTracker("UA-6166990-1"); pageTracker._trackPageview();