2014-11-23 1 views
0

Я работаю над большим проектом на C++ под Visual Studio 2010 и думаю, что внутри есть некоторые утечки памяти. Я пробовал этот подход с включением crtdbg.h, но это не очень помогает, так как я не вижу места утечки. Определение нового имеет 2 ловушки: сначала это нужно делать в каждом файле cpp, который на самом деле не является опцией, а второй он прерывается, например. Увеличение. Использование нового (nothrow) или всего, что использует boosts, has_new_operator.h, нарушает это. [EDIT: он не может скомпилироваться, так как переопределенный «новый» не имеет перегрузок для чего-то вроде «nothrow» или «boost magic»] (если только один не определяет «новый» после всех форвардных заголовков, включая заголовки, ссылающиеся на boost)Обнаружение утечек памяти в Visual C++ (Windows)

Последнее но не в последнюю очередь: у меня есть синглеты. Они реализованы с использованием подклассов шаблона singleton и статической функциональной переменной. Один из них представляет собой контейнер конфигурации, в котором один регистрирует настройки (пары строк и int, которые хранятся на картах). Поскольку дамп утечки mem вызывается до освобождения экземпляра singleton, я получаю огромное количество утечек для всех этих строк и одиночный сам.

Любой способ показать только реальные утечки или сбросить его после освобождения статического объекта?

Какие бесплатные инструменты могут обрабатывать этот случай?

+0

Есть инструменты, которые не являются бесплатными, но обеспечивают полнофункциональную пробную версию. Поэтому вам не нужно тратить свои деньги, чтобы проверить утечки. –

+0

Не совсем уверен, что это именно то, что вам нужно, но valgrind http://valgrind.org/docs/manual/mc-manual.html предоставляет инструменты для устранения утечки памяти. – Vincent

ответ

2

Я использовал визуальный детектор утечки с вполне положительными результатами. Это небольшое и аккуратное, и может быть встроено в ваш проект (если у вас есть текущая конфигурация Debug) в считанных секундах:

https://vld.codeplex.com/

Если правильно установить вверх (что может быть сделано с помощью программы установки), то у вас есть только

#include <vld.h> 

в одной ваших файлов .cpp для каждого модуля - это его заголовок будет делать увязку для вас. Вам не нужно класть его повсюду. Внутри инструмент использует CrtDbg, поэтому для его работы необходимо иметь отладочную сборку.

Он дает вам отладчик или текстовый вывод после каждого запуска (если он настроен с использованием файла конфигурации), даже если он не запускается через отладчик. Это не самый мощный инструмент, но они обычно стоят немного денег;)

EDIT: Существует возможность включения VLD также в неотладочных конфигурациях путем определения VLD_FORCE_ENABLE перед включением заголовка. Но тогда результаты могут быть смягчены.

EDIT: Я попробовал свежую установку VLD. Обратите внимание, что для компиляторов VS2013 необходимо использовать версию v2.4rc2 (или что-то большее v2.3). Версия v2.3 работает только до компиляторов VS2010.

После установки я создал новый проект и установил свои каталоги включений и библиотек, чтобы включить соответствующие папки VLD. После этого я использовал следующий код для проверки memleak отчеты одиночек (обратите внимание, что этот код не имеет смысла, это только доказывает точку):

#include <iostream> 
#include <string> 
#include <sstream> 
#include <map> 

// Uncomment this, if you want VLD to work in non-debug configurations 
//#define VLD_FORCE_ENABLE 

#include <vld.h> 

class FooSingleton { 
    private: 
     std::map<std::string, std::string*> 
      _map; 

     FooSingleton() { 
     } 

    public: 
     static FooSingleton* getInstance(void) { 
      /* THIS WOULD CAUSE LEAKS TO BE DETECTED 
       SINCE THE DESTRUCTOR WILL NEVER BE CALLEd 
       AND THE MAP IS NOT CLEARED. 
      */ 
      // FooSingleton* instance = new FooSingleton; 
      // return instance; 

      static FooSingleton instance; 
      return &instance; 
     } 

     void addString(const std::string& val) { 
      _map.insert(std::make_pair(val, new std::string(val))); 
     } 

     ~FooSingleton(void) { 
      auto it = _map.begin(); 
      auto ite = _map.end(); 

      for(; it != ite; ++it) { 
       delete it->second; 
      } 
     } 
}; 

int main(int argc, char** argv) { 
    FooSingleton* fs = FooSingleton::getInstance(); 
    for(int i = 0; i < 100; ++i) { 
     std::stringstream ss; 
     ss << i << "nth string."; 
     fs->addString(ss.str()); 
    } 

    return 0; 
} 

С помощью этого кода, то VLD не сообщать о любых утечек, потому что статическая автоматическая переменная в getInstance() будет уничтожена после выхода, а элементы на карте будут удалены. Это должно быть сделано, тем не менее, даже если это синглтон, иначе будут сообщены утечки.Но в этом случае:

Visual Leak Detector Version 2.3 installed. 
Aggregating duplicate leaks. 
Outputting the report to the debugger and to D:\dev\projects\tmp\memleak\memleak\memory_leak_report.txt 
No memory leaks detected. Visual Leak Detector is now exiting. 

Если код в getInstance() изменяется на закомментированное версии, то одноплодный никогда не прояснились, и следующие утечки (среди прочих) сообщаются:

---------- Block 11 at 0x008E5928: 52 bytes ---------- 
Leak Hash: 0x973608A9 Count: 100 
    Call Stack: 
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory (36): memleak.exe!std::_Allocate<std::_Tree_nod<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::alloca + 0x15 bytes 
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory (187): memleak.exe!std::allocator<std::_Tree_nod<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::alloca + 0xB bytes 
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (560): memleak.exe!std::_Tree_val<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,s + 0xD bytes 
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (588): memleak.exe!std::_Tree_val<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,s + 0x8 bytes 
    c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree (756): memleak.exe!std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std:: + 0x17 bytes 
    d:\dev\projects\tmp\memleak\memleak\main.cpp (33): memleak.exe!FooSingleton::addString + 0xA9 bytes 
    d:\dev\projects\tmp\memleak\memleak\main.cpp (51): memleak.exe!main + 0x37 bytes 
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (555): memleak.exe!__tmainCRTStartup + 0x19 bytes 
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (371): memleak.exe!mainCRTStartup 
    0x76BF919F (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0xE bytes 
    0x7739A22B (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x84 bytes 
    0x7739A201 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x5A bytes 
    Data: 
    C0 53 8E 00 30 67 8E 00 C0 53 8E 00 98 58 8E 00  .S..0g.. .S...X.. 
    30 6E 74 68 20 73 74 72 69 6E 67 2E 00 CD CD CD  0nth.str ing..... 
    0C 00 00 00 0F 00 00 00 CD CD CD CD 48 56 8E 00  ........ ....HV.. 
    01 00 CD CD  

Вы можете четко видеть Count: 100 для этого блока кода, что является правильным.

Я также отредактировал мой vld.ini файл в каталоге установки, чтобы иметь следующий набор должен быть включен:

AggregateDuplicates = yes 
ReportTo = both 

Они убедитесь, что а) все повторяющиеся утечки раздавлены вместе один отчет с утечкой подсчета (как указано выше, иначе будет 100 записей), а другой, чтобы файл отчета был сброшен в каталог приложения.

Таким образом, для одиночных игр он работает нормально, если вы используете подход статической автоматической переменной, который вы используете, и выполняете очистку в деструкторе.

EDIT: Кроме того, прибор может быть отключен на определенных частях кода. Если приведенный выше код будет изменен следующим образом:

void addString(const std::string& val) { 
    VLDDisable(); 
    _map.insert(std::make_pair(val, new std::string(val))); 
    VLDEnable(); 
} 

Утечки не никогда профилировать и не отслеживаются.

+0

Если он использует crtDbg, он обрабатывает одиночные игры? Есть ли что-нибудь, что я могу сделать, чтобы синглтоны больше не показывались как утечки?(Черт, я бы полюбил что-то вроде valgrind для окон ...) – Flamefire

+0

@Flamefire Я думаю, что синглеты появятся, потому что они никогда не удаляются, если они динамически создаются в 'getInstance()'. Я не могу сказать точно, если честно, вам придется проверить это, извините. Но предполагая, что вы ожидаете, что это утечки, они не должны беспокоить вас и могут быть отфильтрованы довольно легко - также потому, что они сообщаются только один раз, и реальные неприятные утечки появляются сотни раз;) – PuerNoctis

+0

@Flamefire Дополнение: Если вы просто верните адрес статической автоматической переменной в свой 'getInstance()' (или, тем не менее, вы обращаетесь к своему синглону), тогда все должно быть хорошо, потому что они будут очищены при выходе. Просто «новый» никогда не будет иметь экземпляр «delete», который может быть проверен VLD. И есть также API, с помощью которого вы можете включить/отключить VLD во время выполнения ('VLDDisable()' и 'VLDEnable()'). Они могут работать, если вы разместите их вокруг выделенного выделения. – PuerNoctis

0

Вы можете получить источник утечки памяти от crtdebug. это не поможет вам с распределением ускорения, если вы не скомпилируете boost (или любую библиотеку) таким же образом, но для остальных он покажет вам файл распределения и строку.

Это, как вы правильно использовать в crtdebug.h:

в верхней части stdafx.h (или любой другой файл PCH) добавить следующие строки:

#ifdef DEBUG 
    //must define both _CRTDBG_MAP_ALLOC and _CRTDBG_MAP_ALLOC_NEW 
    #define _CRTDBG_MAP_ALLOC 
    #define _CRTDBG_MAP_ALLOC_NEW 

    #include <stdlib.h> 
    #include <crtdbg.h> 
    //if you won't use this macro you'll get all new as called from crtdbg.h  
    #define DEBUG_NEW new(_CLIENT_BLOCK, __FILE__, __LINE__) 
    #define new DEBUG_NEW 
#endif 

Теперь в начале вашего main или winmain или любая точка входа в вашу программу, добавьте следующие строки:

//register memory leak check at end of execution: 
//(if you use this you won't need to use _CrtDumpMemoryLeaks at the end of your main) 
_CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 
//set report mode: 
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); 

Теперь здесь небольшой го я сделал:

После новой консольной программы из VS10 под названием "тест": Мой stdafx.h:

#pragma once 

#ifdef _DEBUG 
    #define _CRTDBG_MAP_ALLOC 
    #define _CRTDBG_MAP_ALLOC_NEW 
    #include <stdlib.h> 
    #include <crtdbg.h> 

    #define DEBUG_NEW new(_CLIENT_BLOCK, __FILE__, __LINE__) 
    #define new DEBUG_NEW 
#endif 

#include "targetver.h" 

#include <stdio.h> 
#include <tchar.h> 

и мой test.cpp является:

#include "stdafx.h" 
void CheckMemoryLeak() 
{ 
    char *ptr=new char[100]; 
    int n=900; 
    sprintf(ptr,"%d",n); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); 

    CheckMemoryLeak(); 

    return 0; 
} 

Выход является:

'tests.exe': Loaded 'C:\Users\shr\Documents\Visual Studio 2010\Projects\tests\Debug\tests.exe', Symbols loaded. 
'tests.exe': Loaded 'C:\Windows\SysWOW64\ntdll.dll', Cannot find or open the PDB file 
'tests.exe': Loaded 'C:\Windows\SysWOW64\kernel32.dll', Cannot find or open the PDB file 
'tests.exe': Loaded 'C:\Windows\SysWOW64\KernelBase.dll', Cannot find or open the PDB file 
'tests.exe': Loaded 'C:\Windows\SysWOW64\msvcr100d.dll', Symbols loaded. 
Detected memory leaks! 
Dumping objects -> 
c:\users\shr\documents\visual studio 2010\projects\tests\tests\tests.cpp(9) : {97} client block at 0x01003288, subtype 0, 100 bytes long. 
Data: <900    > 39 30 30 00 CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete. 
The program '[1600] tests.exe: Native' has exited with code 0 (0x0). 
+0

Как упоминалось в вопросе: Это не поможет. Мне нужно сделать это в каждом файле cpp проекта или включить заголовок в каждый файл cpp (stdafx.h не используется последовательно). Также он не во многих случаях (boost, nothrow, ...) из-за макроса , – Flamefire

+0

1. Если вы скомпилируете boost или любую другую библиотеку с этим, то она также обнаружит утечки памяти в библиотеках. 2. По умолчанию Visual Studio использует pre-compiled-header, вы также можете исправить «crtdbg.h», но в любом случае вам нужно будет включить его в начало любого исходного файла, что является той же самой работой. – SHR

+0

Извините, я не был достаточно ясен: используя boost с этой компиляцией! И как уже упоминалось: я не могу изменить весь большой проект, чтобы проверить наличие утечек. – Flamefire