Localizando perdas de memória usando a biblioteca CRT
Vazamentos de memória, definidos como a falha em desalocar corretamente a memória anteriormente alocada, estão entre os bugs mais sutis e difíceis de detectar em aplicativos C/C++.Um vazamento de memória pequeno não pode ser observado no início, mas ao longo do tempo, um vazamento de memória progressivo pode causar os sintomas que variam de desempenho reduzido a falhar quando o aplicativo é executado sem memória.Pior, um aplicativo de escape que usa toda a memória disponível pode causar a falha de outro aplicativo, criando a confusão a respeito de que o aplicativo é responsável.Até mesmo vazamentos de memória aparentemente inofensivos podem ser sintomáticos de outros problemas que devem ser corrigidos.
O depurador do Visual Studio e as bibliotecas em tempo de execução (CRT) do C fornecem os meios para detectar e identificar vazamentos de memória.
Habilitando a detecção de vazamento de memória
As principais ferramentas para detectar vazamentos de memória são o depurador e o CRT funções heap de depuração.
Para ativar as funções da heap de depuração, inclua as seguintes instruções em seu programa:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
Para que as funções CRT funcionem corretamente, as instruções #include devem seguir a ordem mostrada aqui.
A inclusão de crtdbg.h mapeia as funções malloc e free às versões de depuração, _malloc_dbg e free, que acompanham a alocação e a desalocação de memória.Esse mapeamento ocorre apenas em compilações de depuração, que tem _DEBUG.Compilações lançadas usam as funções malloc e free.
A declaração de #define mapeia uma versão de base de funções heap de CRT para a versão de depuração correspondente.Se você omitir a instrução #define, o despejo de vazamento de memória será menos detalhado.
Após ser habilitado, o heap de depuração funciona usando essas declarações. Você poderá fazer uma chamada a _CrtDumpMemoryLeaks antes de um ponto de saída do aplicativo para exibir um relatório de vazamento de memória quando seu aplicativo for encerrado:
_CrtDumpMemoryLeaks();
Se o seu aplicativo tiver várias saídas, não será necessário posicionar manualmente uma chamada para _CrtDumpMemoryLeaks em cada ponto de saída.Uma chamada para _CrtSetDbgFlag no início do seu aplicativo causará uma chamada automática para _CrtDumpMemoryLeaks em cada ponto de saída.Você deve definir os dois campos de bits mostrados aqui:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
Por padrão, a saída _CrtDumpMemoryLeaks emite o relatório de vazamento de memória para o painel Depurar da janela Saída.Você pode usar _CrtSetReportMode para redirecionar o relatório para outro local.
Se você usar uma biblioteca, a biblioteca poderá redefinir a saída para outro local.Nesse caso, você poderá definir o local de saída de volta para a janela Saída, como mostrado aqui:
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );
Interpretando o relatório de vazamento de memória
Se o seu aplicativo não definir _CRTDBG_MAP_ALLOC, _CrtDumpMemoryLeaks exibirá um relatório de vazamento de memória parecido com este:
Detected memory leaks!
Dumping objects ->
{18} normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
Se o seu aplicativo definir _CRTDBG_MAP_ALLOC, o relatório de vazamento de memória se parecerá com este:
Detected memory leaks!
Dumping objects ->
C:\PROGRAM FILES\VISUAL STUDIO\MyProjects\leaktest\leaktest.cpp(20) : {18}
normal block at 0x00780E80, 64 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
A diferença é que o segundo relatório mostra o nome do arquivo e número da linha no qual a memória vazada é atribuída primeiro.
Se você definir _CRTDBG_MAP_ALLOC ou não, o relatório de vazamento de memória exibirá as seguintes informações:
O número de alocação de memória, que é 18 nesse exemplo
O tipo de bloco, que é normal neste exemplo.
O número de alocação de memória hexadecimal, que é 0x00780E80 nesse exemplo.
O tamanho do bloco, 64 bytes neste exemplo.
Os primeiros 16 bytes de dados no bloco, no formulário hexadecimal.
O relatório de vazamento de memória identifica um bloco de memória como normal, cliente, ou CRT.Um bloco normal é a memória comum atribuída pelo seu programa.Um bloco de cliente é um tipo especial de bloco de memória usado por programas MFC para os objetos que exigem um destruidor.O operador MFC new cria um bloco normal ou um bloco de cliente, como apropriado para o objeto que estiver sendo criado.Um bloco de CRT é atribuído pela biblioteca de CRT para seu próprio uso.A biblioteca de CRT trata a desalocação desses blocos.Portanto, é improvável que você os veja no relatório de vazamento de memória a menos que algo esteja significativamente errado, por exemplo, a biblioteca de CRT está danificada.
Há dois tipos de outros blocos de memória que nunca aparecem nos relatórios de escape de memória.Um bloco livre é a memória que foi liberada.Isso significa que não está vazado, por definição.Um bloco ignorar é a memória que você marcou explicitamente para excluir do relatório de vazamento de memória.
Essas técnicas funcionam para a memória alocada usando a função de CRT malloc padrão.Se seu programa aloca memória usando o operador new do C++, no entanto, você precisará redefinir new se quiser ver o arquivo e a números de linha em um relatório de vazamento de memória.Você pode fazer isso com um bloco de código que tem esta aparência:
#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif // _DEBUG
Pontos de interrupção em um Número de Alocação de Memória
O número de alocação de memória informa quando um bloco de memória vazado tiver sido atribuído.Um bloco com uma alocação de memória de 18, por exemplo, é o 18º bloco de memória alocado durante a execução do aplicativo.O relatório de CRT conta todas as alocações bloco de memória durante a execução.Isso inclui as alocações pela biblioteca de CRT e por outras bibliotecas, como o MFC.Portanto, um bloco com um número de alocação de memória de 18 não pode ser o 18º bloco de memória atribuído pelo seu código.Normalmente, não será.
Você pode usar o número de alocação para definir um ponto de interrupção na alocação da memória.
Para definir um ponto de interrupção de alocação de memória usando a janela Inspeção
Defina um ponto de interrupção no início do seu aplicativo, e depois inicie seu aplicativo.
Quando o aplicativo para no ponto de interrupção, a janela Inspeção.
Na janela Inspeção, digite _crtBreakAlloc na coluna Nome.
Se estiver usando a versão com multithread da DLL da biblioteca CRT (a opção /MD), inclua o operador de contexto: {,,msvcr120d.dll}_crtBreakAlloc
Pressione RETURN.
O depurador avalia a chamada e coloca o resultado na coluna de Valor.Esse valor será -1 se você não definir nenhum ponto de interrupção nas alocações de memória.
Na coluna Valor, substitua o valor mostrado com o número de alocação da alocação de memória onde você deseja interromper.
Após definir um ponto de interrupção em um número de alocação de memória, você poderá continuar a depuração.Tenha cuidado ao executar o programa nas mesmas condições que a execução anterior de modo que a ordem de alocação de memória não seja alterada.Quando o programa interrompe a alocação de memória especificada, você pode usar a janela Pilha de Chamadas e outras janelas de depuração para determinar as condições em que a memória foi atribuída.Em seguida, você pode continuar a execução para observar o que acontece ao objeto e determinar o motivo dele não ser deslocado corretamente.
Definindo um ponto de interrupção de dados no objeto também pode ser útil.Para obter mais informações, consulte Definir um ponto de interrupção de alteração de dados (somente C++ nativo).
Você também pode definir pontos de interrupção de alocação de memória no código.Há duas formas de fazer isso:
_crtBreakAlloc = 18;
ou:
_CrtSetBreakAlloc(18);
Comparando estados de memória
Outra técnica para localizar vazamentos de memória envolve pegar instantâneos do estado da memória do aplicativo em pontos-chave.Para obter um instantâneo do estado da memória em um determinado ponto em seu aplicativo, crie uma estrutura _CrtMemState e passe-a para a função de _CrtMemCheckpoint.Esta função preenche a estrutura com uma forma instantânea do estado de memória atual:
_CrtMemState s1;
_CrtMemCheckpoint( &s1 );
_CrtMemCheckpoint preencha a estrutura com uma forma instantânea do estado de memória atual.
Para exibir o conteúdo de uma estrutura de _CrtMemState, passe a estrutura para a função de _ CrtMemDumpStatistics:
_CrtMemDumpStatistics( &s1 );
saída de**_ CrtMemDumpStatistics** resulta num despejo do estado de memória que parece com este:
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
3071 bytes in 16 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 3071 bytes.
Total allocations: 3764 bytes.
Para determinar se um vazamento de memória ocorreu em uma seção de código, você pode capturar instantâneos do estado da memória antes e após a seção e, em seguida, usar _ CrtMemDifference para comparar os dois estados:
_CrtMemCheckpoint( &s1 );
// memory allocations take place here
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );
_CrtMemDifference compara os estados de memória s1 e s2 e retorna um resultado em (s3) que é a diferença de s1 e de s2.
Uma técnica para localizar vazamentos de memória começa colocando chamadas de _CrtMemCheckpoint no início e fim do seu aplicativo, depois usando _CrtMemDifference para comparar os resultados.Se _CrtMemDifference mostra um vazamento de memória, você pode adicionar mais chamadas de _CrtMemCheckpoint para dividir seu programa usando uma busca binária até ter isolado a fonte do vazamento.
Falsos positivos
Em alguns casos, _CrtDumpMemoryLeaks poderá dar falsas indicações de vazamentos de memória.Isso pode ocorrer se você usar uma biblioteca que marca alocações internas como _NORMAL_BLOCKs em vez de _CRT_BLOCKs ou de _CLIENT_BLOCKs.Nesse caso, _CrtDumpMemoryLeaks não é capaz de reconhecer a diferença entre alocações de usuário e alocações internas de biblioteca.Se os destruidores globais das alocações de biblioteca forem executados após o ponto onde você chama _CrtDumpMemoryLeaks, cada alocação interna da biblioteca será relatada como um vazamento de memória.As versões anteriores de biblioteca padrão do modelo, anteriores do Visual Studio .NET, causaram _CrtDumpMemoryLeaks para relatar falsos positivos, mas este foram corrigidos em versões recentes.