在本篇中,我們需要為其加入內(nèi)存泄漏的檢測代碼,以此來檢測代碼編寫過程中的疏忽帶來的內(nèi)存泄漏。(callstack的顯示暫時僅支持Windows)
一、內(nèi)存泄漏檢測
首先,改寫obj和block結(jié)構(gòu),在obj中加入一個域released表示這個chunk是否被釋放
1 struct obj
2 {
3 #ifdef _DEBUG
4 bool released;
5
6 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) // Only windows can get callstack
7 #define CALLSTACK_MAX_DEPTH 30
8 UINT_PTR callStack[CALLSTACK_MAX_DEPTH];
9 DWORD dwCallStackDepth; // Real depth
10 #endif
11
12 #endif
13 obj* next;
14 };
15
16 struct block
17 {
18 block* next;
19 void* data;
20 #ifdef _DEBUG
21 size_type size;
22 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
23 UINT_PTR callStack[CALLSTACK_MAX_DEPTH];
24 DWORD dwCallStackDepth;
25 #endif
26 #endif
27 };
其中的callstack部分將在下一節(jié)中介紹2 {
3 #ifdef _DEBUG
4 bool released;
5
6 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) // Only windows can get callstack
7 #define CALLSTACK_MAX_DEPTH 30
8 UINT_PTR callStack[CALLSTACK_MAX_DEPTH];
9 DWORD dwCallStackDepth; // Real depth
10 #endif
11
12 #endif
13 obj* next;
14 };
15
16 struct block
17 {
18 block* next;
19 void* data;
20 #ifdef _DEBUG
21 size_type size;
22 #if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
23 UINT_PTR callStack[CALLSTACK_MAX_DEPTH];
24 DWORD dwCallStackDepth;
25 #endif
26 #endif
27 };
然后,我們增加一個結(jié)構(gòu)
1 #ifdef _DEBUG
2 struct use
3 {
4 obj* data;
5 use* next;
6 };
7 #endif
其中data域指向了一塊分配出去的小內(nèi)存塊,next域形成了一張鏈表。2 struct use
3 {
4 obj* data;
5 use* next;
6 };
7 #endif
然后,我們添加一個成員變量來保存這張鏈表,以及一個函數(shù)來將一個chunk插入這張鏈表
#ifdef _DEBUG
use* use_list;
#endif
#ifdef _DEBUG
inline void MemoryPool::addUseInfo(obj* ptr)
{
use* p = (use*)malloc(sizeof(use));
p->data = ptr;
p->next = use_list;
use_list = p;
}
#endif
use* use_list;
#endif
#ifdef _DEBUG
inline void MemoryPool::addUseInfo(obj* ptr)
{
use* p = (use*)malloc(sizeof(use));
p->data = ptr;
p->next = use_list;
use_list = p;
}
#endif
然后,我們來改寫refill函數(shù)使其在分配內(nèi)存塊時打上released標(biāo)記,并將每個分配的內(nèi)存塊記錄下來
1 void* MemoryPool::refill(int i, void(*h)(size_type))
2 {
3 const int count = 20;
4 const int preSize = (i + 1) * ALIGN + headerSize;
5 char* p = (char*)malloc(preSize * count);
6 while(p == 0)
7 {
8 h(preSize * count);
9 p = (char*)malloc(preSize * count);
10 }
11 block* pBlock = (block*)malloc(sizeof(block));
12 while(pBlock == 0)
13 {
14 h(sizeof(block));
15 pBlock = (block*)malloc(sizeof(block));
16 }
17 pBlock->data = p;
18 pBlock->next = free_list;
19 free_list = pBlock;
20
21 obj* current = (obj*)p;
22 #ifdef _DEBUG
23 addUseInfo(current);
24 current->released = false;
25 #endif
26 current = (obj*)((char*)current + preSize);
27 for(int j = 0; j < count - 1; ++j)
28 {
29 #ifdef _DEBUG
30 addUseInfo(current);
31 current->released = true;
32 #endif
33 current->next = chunk_list[i];
34 chunk_list[i] = current;
35 current = (obj*)((char*)current + preSize);
36 }
37 return (char*)p + headerSize;
38 }
其中的headerSize跟callstack有關(guān),將在下一節(jié)中介紹。2 {
3 const int count = 20;
4 const int preSize = (i + 1) * ALIGN + headerSize;
5 char* p = (char*)malloc(preSize * count);
6 while(p == 0)
7 {
8 h(preSize * count);
9 p = (char*)malloc(preSize * count);
10 }
11 block* pBlock = (block*)malloc(sizeof(block));
12 while(pBlock == 0)
13 {
14 h(sizeof(block));
15 pBlock = (block*)malloc(sizeof(block));
16 }
17 pBlock->data = p;
18 pBlock->next = free_list;
19 free_list = pBlock;
20
21 obj* current = (obj*)p;
22 #ifdef _DEBUG
23 addUseInfo(current);
24 current->released = false;
25 #endif
26 current = (obj*)((char*)current + preSize);
27 for(int j = 0; j < count - 1; ++j)
28 {
29 #ifdef _DEBUG
30 addUseInfo(current);
31 current->released = true;
32 #endif
33 current->next = chunk_list[i];
34 chunk_list[i] = current;
35 current = (obj*)((char*)current + preSize);
36 }
37 return (char*)p + headerSize;
38 }
當(dāng)然,在deallocate時要將此內(nèi)存塊的released標(biāo)記打為true
1 void MemoryPool::deallocate(void* p, size_type n)
2 {
3 if(p == 0) return;
4 if(n > MAX_BYTES)
5 {
6 free(p);
7 return;
8 }
9 const int i = INDEX(ROUND_UP(n));
10 #ifdef _DEBUG
11 p = (char*)p - (int)headerSize;
12 obj* ptr = reinterpret_cast<obj*>(p);
13 if (ptr->released) throw error<char*>("chunk has already released", __FILE__, __LINE__);
14 ptr->released = true;
15 #endif
16 reinterpret_cast<obj*>(p)->next = chunk_list[i];
17 chunk_list[i] = reinterpret_cast<obj*>(p);
18 }
2 {
3 if(p == 0) return;
4 if(n > MAX_BYTES)
5 {
6 free(p);
7 return;
8 }
9 const int i = INDEX(ROUND_UP(n));
10 #ifdef _DEBUG
11 p = (char*)p - (int)headerSize;
12 obj* ptr = reinterpret_cast<obj*>(p);
13 if (ptr->released) throw error<char*>("chunk has already released", __FILE__, __LINE__);
14 ptr->released = true;
15 #endif
16 reinterpret_cast<obj*>(p)->next = chunk_list[i];
17 chunk_list[i] = reinterpret_cast<obj*>(p);
18 }
OK,現(xiàn)在已經(jīng)有模有樣了,可以松口氣了。接下來是最重要的部分,在MemoryPool析構(gòu)時檢測這個Pool內(nèi)的use_list中是否有chunk的released標(biāo)記為true(內(nèi)存泄漏了)
1 MemoryPool::~MemoryPool()
2 {
3 #ifdef _DEBUG
4 while (use_list)
5 {
6 use *ptr = use_list, *next = use_list->next;
7 if (!ptr->data->released)
8 {
9 obj* pObj = ptr->data;
10 Console::SetColor(true, false, false, true);
11 throw error<char*>("chunk leaked", __FILE__, __LINE__);
12 }
13 free(ptr);
14 use_list = next;
15 }
16 #endif
17 clear();
18 }
其實說來也容易,只需要檢測每個chunk的released標(biāo)記是否為true就行了,而最后的clear函數(shù)是以前析構(gòu)函數(shù)的代碼,用來釋放所有申請的block和大塊的chunk。2 {
3 #ifdef _DEBUG
4 while (use_list)
5 {
6 use *ptr = use_list, *next = use_list->next;
7 if (!ptr->data->released)
8 {
9 obj* pObj = ptr->data;
10 Console::SetColor(true, false, false, true);
11 throw error<char*>("chunk leaked", __FILE__, __LINE__);
12 }
13 free(ptr);
14 use_list = next;
15 }
16 #endif
17 clear();
18 }
OK,現(xiàn)在我們已經(jīng)可以檢測出沒有被deallocate的chunk了。
二、callstack
首先,我們先來看一個Windows API,“CaptureStackBackTrace”這個API通過傳入的一個數(shù)組來得到一組地址。當(dāng)然有這個API并不夠,我們還需要知道是哪個文件的第幾行。“SymGetSymFromAddr64”這個API用來獲取某個地址對應(yīng)的函數(shù)名,“SymGetLineFromAddr64”這個API則是用來獲取某個地址對應(yīng)的文件名和行號的,這兩個函數(shù)的32位版本則是不帶64的。有了這些Windows API,我們就可以很輕松的獲取到當(dāng)前函數(shù)的調(diào)用堆棧了,主要的功勞還是要歸功于Windows強大的dbghelp。
最后,完整的代碼你可以在http://code.google.com/p/qlanguage/中找到。