歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> linux中內存洩漏的檢測(三)定制化的new/delete

linux中內存洩漏的檢測(三)定制化的new/delete

日期:2017/3/1 12:17:52   编辑:關於Linux

《linux中內存洩漏的檢測(二)定制化的malloc/free》中的__wrap方法只解決了C的問題,這一節介紹怎麼讓C++中的new/delete也能方便地插入計數代碼。

wrap方法嘗試

可不可以使用__wrap_new/__wrap_delete?我們試試看。

我寫了這樣的測試代碼

#include 
using namespace std;

int count = 0;

void * __wrap_new(size_t size)
{
    count++;
    return __real_new(size);
}

void __wrap_delete(void *ptr)
{
    count--;
    __real_delete(ptr);
}

int main()
{
    count = 0;
    int *p1 = new int;
    int *p2 = new int;
    delete p1;
    if(count != 0)
        cout<<"memory leak!"<

然後這樣編譯,g++ -o test test.cpp -Wl,--wrap,new -Wl,--wrap,delete,結果

cpptest.cpp: In function ‘void* __wrap_new(size_t)’:
cpptest.cpp:9:27: error: ‘__real_new’ was not declared in this scope
     return __real_new(size);
                           ^
cpptest.cpp: In function ‘void __wrap_delete(void*)’:
cpptest.cpp:15:22: error: ‘__real_delete’ was not declared in this scope
     __real_delete(ptr);
     ^

看來這種方法不可行,這要從new和malloc的區別說起。

new VS. malloc

malloc很好理解,它的作用就是分配一段指定大小的內存空間。

而new的工作分為兩步:

第一步也是分配一段指定大小的內存空間,這一步與malloc相同,它有一個專用的名字,叫operator new

第二步是將分配到的內存以指定的方式初始化化,這是malloc所沒有的,它也有一個專用的名字,叫placement new

步驟 作用 與malloc的關系 是否可以重載 怎樣使用 operator new 分配一段指定大小的空間 相當於malloc 可以重載 可以單獨調用,如class *pA = operator new(100),相當於class *pA = malloc(100); placement new 將一段空間以指定的方式初始化 malloc不能提供這樣的功能 不能重載 可以把空間的指針作為參數傳入,單獨調用這一行為執行初始化操作,如class *pA = new(buf) class();,相當於使用class::class()初始化buf這段內存

關於operator new和placement new和更多細節,可以參考更多文章,但顯然new的功能非常復雜,並不是一個__wrap_new(size_t size)能解決的。

operator new 重載

new的功能雖然復雜,但我們所關心的只是其中與分配內存相關的部分,也就是operator new。幸好,它可以重載。

C++支持重載,我們可以重載new中的operater new,在其中加入計數功能,並通過malloc實現內存申請。

#include 
using namespace std;

#include 
#include 

int count = 0;

void * operator new(size_t size)
{
    count++;
    return malloc(size);
}

void operator delete(void *ptr)
{
    count--;
    free(ptr);
}

int main()
{
    count = 0;
    int *p1 = new int;
    int *p2 = new int;
    delete p1;
    if(count != 0)
        cout<<"memory leak!"<

既然new也是通過調用malloc實現的,那麼也不用operator new和malloc分別統計了,只需要統計malloc就行了。因為__wrap_symbol__real_symbol都是C函數,所有要使用extern "C"

#include 
using namespace std;

#include 
#include 

int count = 0;

extern "C"
{
void* __real_malloc(int c); 
void * __wrap_malloc(int size)
{
    count++;
    return __real_malloc(size);
}

void __real_free(void *ptr);
void __wrap_free(void *ptr)
{
    count--;
    __real_free(ptr);
}
}

void * operator new(size_t size)
{
    return malloc(size);
}

void operator delete(void *ptr)
{
    free(ptr);
}

int main()
{
    count = 0;
    int *p1 = new int(3);
    int *p2 = new int(4);
    cout<<*p1<<' '<<*p2<

分析

優點

(1)使用方便 — 不需要改產品代碼,只需要修改編譯選項即可完成。

(2)范圍全面 — wrap是個鏈接選項,對所有通過__wrap_malloc和__wrap_free鏈接到一起的文件都起作用,不論是靜態庫還是動態庫。

(3)c的檢測與c++的檢測無縫兼容

缺點

(1)該方法要求運行結束時對運行中產生的打印分析才能知道結果。

(2)只能檢測是否洩漏,卻沒有具體信息,比如洩漏了多少空間

(3)不能說明是哪一行代碼引起了洩漏

(4)這一方法雖然解決了C++的替換問題,卻引入了新的問題。因為在C++中對於同一指針申請和釋放,申請和釋放的大小卻有可能不相等,導致有些情況的內存洩漏檢測不到。比如(a)申請子類而析構父類(b)申請數組而釋放數組第一項

改進

欲知如何解決,且看下回分解

Copyright © Linux教程網 All Rights Reserved