2016-03-05 17 views
1

如何縮小原因以找出此Visual Leak Detector輸出中報告的內存泄漏原因?如何解釋或縮小此可視泄漏檢測器輸出中內存泄漏的原因?

問題不在於爲我調試這個特定的代碼,而在於如何處理這樣的問題。視覺泄漏檢測器報告了許多泄漏,像這樣的問題在這裏非常頻繁,所以我希望這個問題的答案不那麼具體,但更一般,這樣不僅有助於解決這個問題,而且更多的解決問題。

---------- Block 305 at 0x00000000FCBFBBB0: 64 bytes ---------- 
    Leak Hash: 0x7DAD966C, Count: 1, Total 64 bytes 
    Call Stack (TID 11728): 
    ucrtbased.dll!malloc() 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\xmemory0 (901): Shady.exe!std::_Wrap_alloc<std::allocator<std::_List_node<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,std::unique_ptr<Texture,std::default_delete<Texture> > >,void * __ptr64> > >::allocate() 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\list (730): Shady.exe!std::_List_alloc<std::_List_base_types<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,std::unique_ptr<Texture,std::default_delete<Texture> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,st() + 0x19 bytes 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\list (716): Shady.exe!std::_List_alloc<std::_List_base_types<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,std::unique_ptr<Texture,std::default_delete<Texture> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,st() 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\list (631): Shady.exe!std::_List_alloc<std::_List_base_types<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,std::unique_ptr<Texture,std::default_delete<Texture> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,st() + 0xC bytes 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\list (818): Shady.exe!std::_List_buy<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,std::unique_ptr<Texture,std::default_delete<Texture> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > con() 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\list (896): Shady.exe!std::list<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,std::unique_ptr<Texture,std::default_delete<Texture> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,s() 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\xhash (197): Shady.exe!std::_Hash<std::_Umap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::unique_ptr<Texture,std::default_delete<Texture> >,std::_Uhash_compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::hash<std() + 0x1A bytes 
e:\programme (x86)\microsoft visual studio 14.0\vc\include\unordered_map (119): Shady.exe!std::unordered_map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::unique_ptr<Texture,std::default_delete<Texture> >,std::hash<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::equal_to<std::basic_string() 
e:\repositories\shady\src\texture\texturemanager.h (39): Shady.exe!TextureManager::TextureManager() + 0x44 bytes 
e:\repositories\shady\src\engine.h (53): Shady.exe!Engine::Engine() + 0x65 bytes 
e:\repositories\shady\src\engine.cpp (97): Shady.exe!main() + 0x1D bytes 
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (75): Shady.exe!invoke_main() 
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (264): Shady.exe!__scrt_common_main_seh() + 0x5 bytes 
f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl (309): Shady.exe!__scrt_common_main() 
f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp (17): Shady.exe!mainCRTStartup() 
KERNEL32.DLL!BaseThreadInitThunk() + 0x22 bytes 
ntdll.dll!RtlUserThreadStart() + 0x34 bytes 
    Data: 
B0 BB BF FC B4 01 00 00 B0 BB BF FC B4 01 00 00  ........ ........ 
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD  ........ ........ 
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD  ........ ........ 
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD  ........ ........ 

engine.cpp 97Engine engine = Engine();它構建這個類的對象

class Engine{ 
    Engine() : _logger("Engine"){} 
    TextureManager _textureManager; 
    Logger mutable _logger; 
}; 

texturemanager.h 39是這個類的TextureManager() :_logger("TextureManager"){};

class TextureManager{ 
TextureManager() :_logger("TextureManager"){}; 
~TextureManager() { 
    for (const auto& kv : _textures) { 
     GLuint h = kv.second->getTextureHandle(); 
     glDeleteTextures(1, &h); 
    } 
} 
std::unordered_map<std::string, std::unique_ptr<Texture>> _textures; 
Logger mutable _logger; 
}; 

我是否正確,所述unordered_map導致瞭解輸出泄漏?由於我試圖完全移至sharedunique pointers,因此沒有與此代碼中涉及的newmalloc直接分配。可以將字符串傳遞給構造函數並重用並存儲在類中的問題?從我讀的std :: string確實管理自己的內存。

+0

你有沒有機會在你的記錄器中有一個'unordered_map'?如果是這樣,那麼如何清理? –

+0

不,但上面的TextureManager有一個無序的地圖。我沒有清理unordered_map本身,因爲我希望它能夠正確清理,或者我錯了嗎? – NoxMortem

+0

假設你的'TextureManager'本身被破壞(這大概發生在'Engine'被破壞,那麼'unordered_map'也應該得到清理。 –

回答

0

內存泄漏檢測的一個常見問題是,在檢測器分析快照時,內存泄漏實質上只是沒有釋放內存。

它看起來像最常見的假陽性內存泄漏警告是靜態或全局變量,它們在創建內存泄漏轉儲時未被釋放,但將在稍後時刻釋放。

如果你不是很深刻地認識到爲什麼一些代碼泄漏,這使得發現實際的泄漏更加乏味和困難。選擇的工具可能會報告沒有的泄漏,但您可能不明白原因。

的第一件事情來檢查任何報告的內存泄漏是

  • 是一個靜態/全局變量的參與嗎?
    • 是否有必要處理它們,或者當上下文被釋放時它們是否會被正確清理?
  • 容器是否被正確清理?
  • 是否涉及繼承?
    • 如果子類的實例存儲在任何基類的指針中,那麼所有的基類都包含適當的虛擬析構函數嗎?
  • 是否分配了內存new,malloc或類似的內存?
    • 此內存是否使用delete,delete[]free或類似的方法清除?

Comment Out Debugging -technique,可以使用內存快照,其中內存泄漏縮小相似。以下示例適用於Visual Studio,但可以使用任何允許內存快照的功能使用相同的想法。

//Create 3 Memory states, where s3 is the difference 
_CrtMemState s1, s2, s3; 
//Snapshot the first time 
_CrtMemCheckpoint(&s1); 
//your questionable code 
//Snapshot the second time 
_CrtMemCheckpoint(&s2); 
//If there is a difference between both states, there is a memory leak 
if _CrtMemDifference(&s3, &s1, &s2) 
    _CrtMemDumpStatistics(&s3); 

這個想法可以被包裝成一個宏和一個類的組合,以允許正確的上下文利用。與#ifdef _DEBUG相結合的方法,只包含代碼爲DEBUG構建您可以留下您的內存泄漏檢測你的代碼而不損失性能爲這些宏將no-op被替換爲non-DEBUG構建和那些應該被任何現代編譯器優化了。

裹宏和與該可被用作一個堆棧沿一類如下:

#include <MemoryLeakDetector.h> 
void foo() { 
    BEGINMEMORYLEAKCHECK(); 
    int * j = new int; 
    ENDMEMORYLEAKCHECK();/*Leak 2 of 4 bytes detected.*/ 
} 
int main(void) { 
    /*Because we use a global stack we can nest our macro use!*/ 
    BEGINMEMORYLEAKCHECK(); 
    BEGINMEMORYLEAKCHECK(); 
    int * i = new int; 
    ENDMEMORYLEAKCHECK();/*Leak 1 of 4 bytes detected.*/ 
    delete i;/*Leak 1 is closed.*/ 
    foo();/*but we generate a leak 2 of 4 bytes inside of foo*/ 
    ENDMEMORYLEAKCHECK();/*Leak 2 of 4 bytes is detected here as well*/ 
} 

用下面MemoryLeakDetector.h其使用std::unique_ptr<MemoryLeakDetector>std::stack妥善處理不同的堆棧上下文。這允許嵌套使用我們的宏。我們使用std::unique_ptr不僅可以避免我們的MemoryLeakDetector代碼泄漏,而且允許我們利用std::move而不是複製對象。

MemoryLeakDetector.h

#pragma once 
#ifdef _DEBUG 
    //#include <vld.h> /* Visual Leak Detector Memory Leak Detection*/ 
    #include <crtdbg.h> /*VS Memory Leak Detection*/ 
    #include <stack> 
    #include <memory> 
    #include <sstream> 
    #include "Windows.h" 

class MemoryLeakDetector { 
public: 
    MemoryLeakDetector() {}; 
    _CrtMemState MEMORYLEAKSTATISTICS1; 
    _CrtMemState MEMORYLEAKSTATISTICS2; 
    _CrtMemState MEMORYLEAKSTATISTICS3; 
    static std::stack<std::unique_ptr<MemoryLeakDetector>>& stack() { 
     static std::stack<std::unique_ptr<MemoryLeakDetector>> s; 
     return s; 
    } 
}; 


#define DBOUT(s)        \ 
do{           \ 
    std::ostringstream os;     \ 
    os << s;         \ 
    OutputDebugString(os.str().c_str()); \ 
}while(0) 

#define BEGINMEMORYLEAKCHECK() do{std::unique_ptr<MemoryLeakDetector> ___memoryleakdetector___ = std::make_unique<MemoryLeakDetector>();\ 
           MemoryLeakDetector::stack().push(std::move(___memoryleakdetector___));\ 
           _CrtMemCheckpoint(&MemoryLeakDetector::stack().top().get()->MEMORYLEAKSTATISTICS1);\ 
           }while(0)   
#define ENDMEMORYLEAKCHECK() do{if(MemoryLeakDetector::stack().size()==0){DBOUT("\n"<<__FILE__<<"("<<__LINE__<<"):"<<"<"<<__FUNCTION__<<" ENDMEMORYLEAKCHECK without BEGINMEMORYLEAKCHECK detected\n");\ 
          break;}\ 
          std::unique_ptr<MemoryLeakDetector> ___memoryleakdetector___ = std::move(MemoryLeakDetector::stack().top());MemoryLeakDetector::stack().pop();\ 
          _CrtMemCheckpoint(&___memoryleakdetector___->MEMORYLEAKSTATISTICS2);\ 
          if (_CrtMemDifference(&___memoryleakdetector___->MEMORYLEAKSTATISTICS3, &___memoryleakdetector___->MEMORYLEAKSTATISTICS1, &___memoryleakdetector___->MEMORYLEAKSTATISTICS2)){\ 
          DBOUT("\n"<<__FILE__<<"("<<__LINE__<<"):"<<"<"<<__FUNCTION__<<"> MLD detected a leak\n");\ 
          _CrtMemDumpStatistics(&___memoryleakdetector___->MEMORYLEAKSTATISTICS3);\ 
          DBOUT("\n\n");\ 
          std::cerr << "MLD Leak detected "<<__FILE__<<"("<<__LINE__<<")"<< std::endl;\ 
          }}while(0) 
#else 
    #define BEGINMEMORYLEAKCHECK() do{}while(0) 
    #define ENDMEMORYLEAKCHECK() do{}while(0) 
    #define DBOUT(s) do{}while(0) 
#endif 
1

如何縮小的原因找到了記憶的原因泄露

這可能是因爲您在STL無意中發現了內存泄漏你的編譯器使用的實現。減少這種情況的最好辦法是儘可能多地消除肇事者,並建立一個SSCCE或MVCE,它只會使用有罪的unordered_map

#include <memory> 
#include <string> 
#include <unordered_map> 

int main() 
{ 
    std::unordered_map<std::string, std::unique_ptr<std::string>> uomap; //1 

    uomap["hello"] = std::make_unique<std::string>("world"); //2 

    return uomap.size() - 1; 
} 

http://ideone.com/a3hIaj

啓動該程序與檢漏儀並確定是否仍然可以看到泄漏與否。如果你這樣做,恭喜你的bug發現,你會想要向你的STL供應商報告錯誤。

順便說一下,如果你正在使用微軟的實現,你會發現有趣的是,一個unordered_map建設涉及多個mallocs的。您可以通過在標記爲//1//2的行上設置斷點並啓動該程序來驗證此情況。

當斷點#1命中,選擇Debug - >新建斷點 - >功能斷點,並設置 '的malloc' 斷點。現在按F5鍵 - 如果你想看到STL/CRT裏面的堆棧軌跡,請檢查「Microsoft Symbol Servers」框並點擊OK。

你會發現,有3-4個調用malloc只是在unordered_map的建設。