2014-11-23 83 views
0

我正在研究Visual Studio 2010下的一個大型C++項目,並認爲裏面有一些內存泄漏。我嘗試了包含crtdbg.h的方法,但沒有多大幫助,因爲我沒有看到發生泄漏的位置。定義新的有兩個缺陷:首先它需要在每個cpp文件中完成,這不是一個真正的選項,而第二個文件需要在例如。促進。使用new(nothrow)或使用boost的任何內容「has_new_operator.h」會打破這一點。 [編輯:它不能編譯,因爲重新定義的「新」沒有像「nothrow」或「提升魔術」的東西超載](除非一個定義「新」後,所有升壓頭,包括參考提升標題)檢測Visual C++中的內存泄漏(Windows)

Last但並非最不重要:我有單身。它們是使用單例模板的子類和一個靜態函數變量實現的。其中一個是一個配置容器,其中一個註冊設置(對映射的字符串和整數,而不是存儲在映射中)由於mem泄露轉儲是在單例實例釋放前調用的,所以我得到大量的泄漏,單身人士本身。

任何方式只顯示真正的泄漏或在靜態對象釋放後轉儲?

哪些免費工具可以處理這種情況?

+0

有些工具不是免費的,但提供了功能完整的試用版。所以你不必花錢來檢查泄漏。 – 2014-11-23 22:01:36

+0

不太確定它是否正是你所需要的,但是valgrind http://valgrind.org/docs/manual/mc-manual.html提供了檢測內存泄漏的工具。 – Vincent 2014-11-24 20:22:54

回答

2

我已經使用了可視化泄漏檢測器,結果相當好。這是小而整潔,可以內置到您的項目(假設你有一個正在運行的調試配置)在幾秒鐘的事:

https://vld.codeplex.com/

如果設置正確(可使用安裝完成),那麼你只需要

#include <vld.h> 

在每個模塊的.cpp文件的一個 - 這是它的頭部會做鏈接給你。你不必把它放在任何地方。該工具在內部使用CrtDbg,因此您必須運行一個調試版本才能運行。

它在每次運行後給出調試器或文本輸出(如果使用配置文件進行配置),即使不通過調試器運行。這不是最強大的工具,但這些通常成本一些硬幣;)

EDIT:有一種可能性,通過包括頭之前定義VLD_FORCE_ENABLE使VLD也在非調試配置。但結果可能會隨着時間而減弱。

編輯:我試過了VLD的全新安裝。請注意,對於VS2013編譯器,必須使用v2.4rc2版本(或更高版本的v2.3)。版本v2.3只能在VS2010編譯器之前運行。

安裝完成後,我創建了一個新項目,並設置我的include和library目錄以包含各自的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個條目),另一個是報告文件被轉儲到應用程序的目錄中。

因此,對於單身人士,只要你使用你正在使用的靜態自動變量方法並在析構函數中進行清理,它就可以正常工作。

編輯:此外,儀器可以在特定的代碼段被禁用。如果上面的代碼將被修改如下:

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

泄漏將永遠被異形,而不是跟蹤。

+0

如果它使用crtDbg,它是否處理單身?有什麼我可以做的,這樣的單身人士不再顯示爲泄漏?(該死的我喜歡valgrind的窗口......) – Flamefire 2014-11-23 09:30:50

+0

@Flamefire我認爲單身人士會出現,因爲如果在'getInstance()'中動態創建它們,它們永遠不會被刪除。我不能肯定地說實話,你必須自己檢查一下,對不起。但假設你期待這些泄漏,他們不應該麻煩你,可以很容易地過濾出來 - 也因爲他們只報告一次,真正令人討厭的泄漏顯示數百倍;) – PuerNoctis 2014-11-23 09:35:48

+0

@Flamefire增加:如果你只是在你的'getInstance()'(或者你訪問你的單例)中返回一個靜態自動變量的地址,那麼它應該沒問題,因爲它們會在退出時清理。只有'new'永遠不會有可以由VLD檢查的對應'delete'。還有一個API,可以在運行時啓用/禁用VLD('VLDDisable()'和'VLDEnable()')。如果你把它們放在你的單例分配中,這些可能會起作用。 – PuerNoctis 2014-11-23 09:39:20

0

您可以從crtdebug獲得內存泄漏源。它不會幫助你進行boost分配,除非你以相同的方式編譯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 

現在,在您mainwinmain的開始或任何您的程序的入口點添加以下行:

//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); 

現在這裏一個小te ST我做:

從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 2014-11-23 20:39:56

+0

1.如果您將編譯boost或任何其他庫,那麼它也會檢測庫中的內存泄漏。 2. visual studio默認使用預編譯頭文件,你可以修改'crtdbg.h',但無論如何,你需要將它包含在任何源文件的頂部,這是相同的工作。 – SHR 2014-11-24 08:48:00

+0

對不起,我還不夠清楚:使用boost可以打破編譯! 如上所述:我無法改變整個大項目只是爲了檢查mem泄漏。 – Flamefire 2014-11-24 20:13:56