2009-01-13 39 views
27

如何追蹤在C++中,特別是那些由new/delete完成的內存分配。對於一個對象,我可以輕鬆覆蓋operator new,但我不確定如何全局覆蓋所有分配,因此它們會通過我的自定義new/delete。這應該不是一個大問題,但我不知道這應該如何完成(#define new MY_NEW?)。如何跟蹤使用C內存分配++(尤其是新/刪除)

一旦這個工作,我會認爲足夠有一個地圖的分配指針/位置的地方,所以我可以跟蹤所有分配當前「活動」,並且 - 在應用程序結束時 - 檢查尚未被釋放的分配。

嗯,這似乎又喜歡的事,肯定已經至少已經做了好幾次,所以任何好圖書館外面(最好是便攜式的)?

+0

沒有通用的現成答案。請提供有關所用操作系統和平臺的更多信息。 – kauppi 2009-01-13 10:59:36

+0

我需要一個至少可以在Linux和Windows上運行的解決方案,並且最好在Mac OS上運行。 – Anteru 2009-01-13 12:27:40

回答

26

我建議你在linux下使用valgrind。它會捕獲不釋放的內存,以及寫入未分配內存等其他錯誤。另一個選擇是mudflap,它告訴你關於沒有釋放內存。使用gcc使用-fmudflap -lmudflap選項,然後用MUDFLAP_OPTIONS=-print-leaks ./my_program啓動程序。

這裏有一些非常簡單的代碼。它不適用於複雜的跟蹤,但打算向您展示如果您要自己實現它,原則上如何實現。像這樣的東西(省略了調用註冊的new_handler和其他細節的東西)。

template<typename T> 
struct track_alloc : std::allocator<T> { 
    typedef typename std::allocator<T>::pointer pointer; 
    typedef typename std::allocator<T>::size_type size_type; 

    template<typename U> 
    struct rebind { 
     typedef track_alloc<U> other; 
    }; 

    track_alloc() {} 

    template<typename U> 
    track_alloc(track_alloc<U> const& u) 
     :std::allocator<T>(u) {} 

    pointer allocate(size_type size, 
        std::allocator<void>::const_pointer = 0) { 
     void * p = std::malloc(size * sizeof(T)); 
     if(p == 0) { 
      throw std::bad_alloc(); 
     } 
     return static_cast<pointer>(p); 
    } 

    void deallocate(pointer p, size_type) { 
     std::free(p); 
    } 
}; 

typedef std::map< void*, std::size_t, std::less<void*>, 
        track_alloc< std::pair<void* const, std::size_t> > > track_type; 

struct track_printer { 
    track_type * track; 
    track_printer(track_type * track):track(track) {} 
    ~track_printer() { 
     track_type::const_iterator it = track->begin(); 
     while(it != track->end()) { 
      std::cerr << "TRACK: leaked at " << it->first << ", " 
         << it->second << " bytes\n"; 
      ++it; 
     } 
    } 
}; 

track_type * get_map() { 
    // don't use normal new to avoid infinite recursion. 
    static track_type * track = new (std::malloc(sizeof *track)) 
     track_type; 
    static track_printer printer(track); 
    return track; 
} 

void * operator new(std::size_t size) throw(std::bad_alloc) { 
    // we are required to return non-null 
    void * mem = std::malloc(size == 0 ? 1 : size); 
    if(mem == 0) { 
     throw std::bad_alloc(); 
    } 
    (*get_map())[mem] = size; 
    return mem; 
} 

void operator delete(void * mem) throw() { 
    if(get_map()->erase(mem) == 0) { 
     // this indicates a serious bug 
     std::cerr << "bug: memory at " 
        << mem << " wasn't allocated by us\n"; 
    } 
    std::free(mem); 
} 

int main() { 
    std::string *s = new std::string; 
     // will print something like: TRACK: leaked at 0x9564008, 4 bytes 
} 

我們必須用我們自己的分配器爲我們的地圖,因爲標準的人會用我們的重載運算符new,這將導致無限遞歸。

確保您是否覆蓋operator new,使用map來註冊您的分配。刪除由新的佈局形式分配的內存也將使用該刪除操作符,因此如果某些代碼不知道已經重載了operator new而不使用您的映射,那麼它會變得棘手,因爲操作符delete會告訴您它沒有被分配,使用std::free釋放內存。

還請注意,因爲Pax也指出他的解決方案,所以只會顯示由使用我們自己定義的運算符new/delete的代碼導致的泄漏。所以如果你想使用它們,把他們的聲明放在一個頭文件中,並將其包含在所有應該被監視的文件中。

+0

偉大的職位。我的例子非常有幫助,可以跟蹤和修復嵌入式設備中的內存泄漏:) – tkarls 2012-08-16 08:53:55

+0

很好的例子!有一點需要注意,這段代碼不是線程安全的,所以在一個多線程環境中(其中`new`和`delete`將會被多個線程調用),你必須保護對'track`映射的訪問,的std :: mutex`。 – gbmhunter 2017-10-13 19:20:27

1

不能直接回答你的問題,但如果你真的只是想在節目結束時得到泄露的堆對象的列表,你可能只是valgrind運行程序。

對於MS VS你可以玩the Debug CRT Heap。不像valgrind那麼簡單,這裏有點太多解釋,但可以做你想做的事情。

+0

是的,我現在使用這些,但我想切換內存分配器(尤其是跟蹤各種類別的內存),所以我需要一個自定義的解決方案。 – Anteru 2009-01-13 10:24:59

7

那麼,你可以重新實現全球運營商new和delete給你你想要的功能,但我建議不要,除非這是跟蹤內存分配的唯一方法,因爲你的平臺的限制例。

內存調試器可用於大多數通用開發平臺。看看PurifyPlus可用於在Windows和各種Unix上工作的商業解決方案,或者可用於在Linux(以及其他可能的操作系統,但我只在Linux上使用它)的開放源代碼解決方案的valgrind

如果您在更換全球運營商的意圖,看看this article

7

您可以通過以下修改在http://www.flipcode.com/archives/How_To_Find_Memory_Leaks.shtml使用的代碼:如果你有一個大的honkin'源文件只給了代碼工作。我把這個問題整理成另一個關於SO的問題(here)。

首先,不要更改stdafx.h,使您在自己的文件中進行修改。

製作一個單獨的頭文件mymemory.h,並把你的函數原型在裏面,例如(注意,這沒有):

inline void * __cdecl operator new(unsigned int size, 
    const char *file, int line); 

也是在這頭,把其他原型AddTrack(),DumpUnfreed()等,#定義,類型定義和EXTERN聲明:

extern AllocList *allocList; 

然後,在新的mymemory.cpp(其中也包括#的mymemory.h),把實際allocList的定義以及所有的rea l函數(不只是原型)並將該文件添加到您的項目中。

然後,#include "mymemory.h"在每一個源文件中,你需要跟蹤內存(可能是所有的人)。由於頭文件中沒有定義,因此在鏈接期間不會出現重複內容,並且因爲聲明存在,所以您也不會收到未定義的引用。

請記住,這不會跟蹤您不編譯的代碼(例如第三方庫)中的內存泄漏,但它應該讓您知道您自己的問題。

3

對於我們的Windows平臺C++項目,我使用VLD,Visual Leak Detector,它實際上很容易實現,當您的應用程序退出時跟蹤和報告內存泄漏 - 它的所有免費和源代碼都可用。該系統可以設置爲以多種方式(磁盤記錄器,IDE,XML等)進行報告,並且對於檢測Windows服務中的泄漏是非常寶貴的,這對於調試始終是一項挑戰。因此,在您尋找便攜式解決方案的同時,如果您希望推出自己的產品,當然可以查看指導來源。希望能幫助到你。

引述網站:

這是一個非常有效的方式,快速 診斷和修復,內存泄漏 C/C++應用程序。

http://dmoulding.googlepages.com/vld

-1

如果你的意思是要做到這一點作爲編程練習,它可能給你更多的有識之士來編寫自己的智能指針類(ES)來代替,並堅持使用他們在整個這一個項目(或項目的模塊)。

3

在Linux中,有至少兩個傳統方法:

  • 的malloc()和free()(和其他存儲相關的功能)是弱符號,這意味着你可以簡單地重新實現它們,你的版本將會被使用。有關實施示例:請參閱電圍欄。
  • 使用LD_PRELOAD環境變量,可以使用包含在LD_PRELOAD環境變量中的庫中的符號覆蓋共享庫中的符號(弱符號和強符號)符號。如果您使用malloc(),free()和friends編譯共享庫,則全部設置完成。電圍欄再一次證明了這一點。

因此,您不僅可以捕獲新的和刪除,還可以使用C風格的內存分配函數。我還沒有在Windows上做過這件事,但我已經看到了重寫DLL如何鏈接的方法(儘管我記得它們有點笨拙)。

但是請注意,除了這些都是有趣的技術外,我建議使用valgrind來做你想要的任何東西。

0

如果你在linux下開發,最好的工具之一(例如檢測內存泄漏,跟蹤代碼中某些地方的分配情況)是valgrind,特別是它的地塊工具。唯一的缺點是程序運行速度較慢(或者較慢),所以它只對調試有用。

0

我注意到很多其他答案都集中在你可以使用的工具上。我已經使用了其中的一些,他們幫助了很多。但是作爲編程練習,並且看到您使用C++,您需要覆蓋全局新建和刪除以及malloc,free和realloc。你會認爲只有重寫新的和刪除就足夠了,但std :: string和其他類可能使用malloc,特別是realloc。

然後,一旦你有這個地方,你可以開始添加標題來檢查內存覆蓋,每個分配記錄堆棧跟蹤等等。總而言之,我建議你使用這裏提到的工具之一,但編寫自己的系統可能很有趣。

+0

我嚴重懷疑std:; string會使用realloc,因爲它必須使用提供的allocator,它不支持realloc。 – Anteru 2009-01-13 12:43:35

23

具體來說,使用valgrind的地塊工具。與memcheck相反,地塊不關心非法使用內存,而是跟蹤隨着時間的推移分配。它能夠有效地測量程序的堆內存使用情況。最好的部分是,你不必編寫任何代碼。嘗試:

http://valgrind.org/docs/manual/ms-manual.html

或者,如果你真的很急:

valgrind --tool=massif <executable> <args> 
ms_print massif.out.<pid> | less 

這會給你分配的圖形隨着時間的推移,和背部走線的大分配內容時發生在哪裏。這個工具最好在Linux上運行,我不知道是否有Windows差異。它確實在OS X上工作。

祝你好運!

0

這並不便宜,但我曾經在我的C++日子裏發現purify是調試泄漏和其他內存問題的最佳工具(它現在歸IBM所有,所以surport下山了)。 Bounds Checker被某些人喜歡,但對於我正在開發的軟件並不適用。

0

您可以使用此link給出一個頭文件(MemTracker.h)添加到您的解決方案來跟蹤在C和C++的內存分配/釋放。它顯示你是否有內存泄漏,以及哪一行代碼負責它。

0

如果我需要一個工具,我通常從我的編譯器/標準庫提供的東西開始。

  • 如果您使用glibc,您可以使用mtrace。它安裝一個全局鉤子,記錄每個glibc的內存分配函數(malloc,realloc,memalign,free以及在它們之上實現的所有東西,比如new/delete)
  • 如果使用Microsoft CRT,則可以查看CRT Debug Heap Details。有實例如何安裝的內存分配函數調試版本,讓堆統計,發現內存泄漏等
0

檢查這個小得心應手代碼,現在不是new使用NEW並跟蹤NewHelper構造所有分配:

#include <iostream> 

class NewHelper 
{ 
    private : 
    void* addr = nullptr; 
     public : 
     NewHelper(void * addr_) 
     { 
      addr = addr_; 
      std::cout<<addr<<std::endl; 
     } 
     template <class T> 
     operator T() 
     { 
      return (T)addr; 
     } 
}; 
#define NEW (NewHelper)(void*)new 
int main() 
{ 
    int * i = NEW int(0); 
return 0; 
}