《linux中內存洩漏的檢測(二)定制化的malloc/free》中的__wrap方法只解決了C的問題,這一節介紹怎麼讓C++中的new/delete也能方便地插入計數代碼。
可不可以使用__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)申請數組而釋放數組第一項
改進
欲知如何解決,且看下回分解