很久沒有寫技術(shù)方面的東西了,這半年主要是在學(xué)習(xí)別人的東西,對自己提高比較大,算是一次技術(shù)回籠吧,這次學(xué)習(xí)之旅目的是結(jié)束技術(shù)方面的專注,開始向應(yīng)用方面找突破口,也就是完成技術(shù)積累或者為技術(shù)的積累做堅實的準(zhǔn)備。
c/C++的一個讓人瘋狂的地方就是內(nèi)存管理,非法訪問、越界、野指針、泄漏、內(nèi)存分配器等諸多問題,有時候一個編程老手也會迷惘困惑。Crt有一些堆棧檢查的函數(shù)可以完成基本的內(nèi)存狀況檢查,MFC也有一些簡單的對象檢查機制,當(dāng)然好的算是java、.net等sdk的超重量級封裝了,即使發(fā)生對象錯誤也能把堆棧信息明明白白的告訴你(至少表面上是這樣,具體我對這兩種語言沒有做過開發(fā))。下面介紹的是某牛公司實現(xiàn)的內(nèi)存分配工具,基本實現(xiàn)了內(nèi)存泄漏檢查,對象合法性檢查,對于我來說已經(jīng)夠用了。
為了對內(nèi)存分配塊進行跟蹤,設(shè)計如下結(jié)構(gòu)體:
//+--------------------------------------------------------------
//
// 每個請求分配內(nèi)存塊的前綴結(jié)構(gòu)體
// 用來跟蹤所有請求分配塊以及請求分配名稱
//
//---------------------------------------------------------------
struct DBGALLOCHDR


{
DBGALLOCHDR* pdbgahPrev; // 前一個內(nèi)存塊頭
DBGALLOCHDR* pdbgahNext; // 后一個內(nèi)存塊頭
DWORD iAllocated; // 記錄是第幾次請求分配操作
DWORD tid; // 請求分配線程的ID
size_t cbRequest; // 請求分配大小
char szName[64]; // 請求分配塊名稱
DWORD adwGuard[4];// 保護頭
};

//+--------------------------------------------------------------
//
// 每個請求分配內(nèi)存塊的后綴結(jié)構(gòu)體
// 使用特定的數(shù)據(jù)填充用來檢測指針是合法
//
//---------------------------------------------------------------
struct DBGALLOCFOOT


{
DWORD adwGuard[4];
};

// 內(nèi)存跟蹤塊的根,通過根可以獲取所有分配塊
DBGALLOCHDR g_dbgahRoot =


{
&g_dbgahRoot,
&g_dbgahRoot,
0,
(DWORD)-1
};
為了實現(xiàn)多線程內(nèi)存分配跟蹤,采用Tls技術(shù)使用線程局部對象保存當(dāng)前分配信息:
// 線程局部對象結(jié)構(gòu)體,輔助實現(xiàn)每個線程的請求內(nèi)存分配記錄
struct DBGTHREADSTATE


{
DBGTHREADSTATE* ptsNext;
DBGTHREADSTATE* ptsPrev;

// Add globals below
void* pvRequest; // 線程最后一次請求分配內(nèi)存的指針
size_t cbRequest; // 線程最后一次請求分配內(nèi)存的大小
};



// 調(diào)試期間實際分配內(nèi)存大小=請求分配+分配頭+分配尾
size_t _ActualSizeFromRequestSize(size_t cb)


{
return cb+sizeof(DBGALLOCHDR)+sizeof(DBGALLOCFOOT);
}
主要實現(xiàn)的內(nèi)存分配工具有如下這些:
void* _MemAlloc(ULONG cb);
void* _MemAllocClear(ULONG cb);
HRESULT _MemRealloc(void** ppv, ULONG cb);
ULONG _MemGetSize(void* pv);
void _MemFree(void* pv);
HRESULT _MemAllocString(LPCTSTR pchSrc, LPTSTR* ppchDst);
HRESULT _MemAllocString(ULONG cch, LPCTSTR pchSrc, LPTSTR* ppchDst);
HRESULT _MemReplaceString(LPCTSTR pchSrc, LPTSTR* ppchDest);

#define MemAlloc(cb) _MemAlloc(cb)
#define MemAllocClear(cb) _MemAllocClear(cb)
#define MemRealloc(ppv, cb) _MemRealloc(ppv, cb)
#define MemGetSize(pv) _MemGetSize(pv)
#define MemFree(pv) _MemFree(pv)
#define MemAllocString(pch, ppch) _MemAllocString(pch, ppch)
#define MemAllocStringBuffer(cch, pch, ppch) _MemAllocString(cch, pch, ppch)
#define MemReplaceString(pch, ppch) _MemReplaceString(pch, ppch)
#define MemFreeString(pch) _MemFree(pch)
通過宏實現(xiàn)類的new delete重寫:
#define DECLARE_MEMALLOC_NEW_DELETE() \

inline void* __cdecl operator new(size_t cb)
{ return(MemAlloc(cb)); } \

inline void* __cdecl operator new[](size_t cb)
{ return(MemAlloc(cb)); } \

inline void __cdecl operator delete(void* pv)
{ MemFree(pv); }

#define DECLARE_MEMCLEAR_NEW_DELETE() \

inline void* __cdecl operator new(size_t cb)
{ return(MemAllocClear(cb)); } \

inline void* __cdecl operator new[](size_t cb)
{ return(MemAllocClear(cb)); } \

inline void __cdecl operator delete(void* pv)
{ MemFree(pv); }
在應(yīng)用的時候可以重寫全局new delete:
// 測試全局new delete

void* __cdecl operator new(size_t cb)
{ return(MemAlloc(cb)); }

void* __cdecl operator new[](size_t cb)
{ return(MemAlloc(cb)); }

void __cdecl operator delete(void* pv)
{ MemFree(pv); }
使用注意:
進程啟動時候需要調(diào)用:
_DbgDllProcessAttach();
_afxGlobalData._hProcessHeap = GetProcessHeap();
進程退出的時候需要調(diào)用:
_DbgDllProcessDetach();
測試用例:
// 測試基本類型
void TestBuiltin()


{
// 基本類型
int* pInt = new int(10);
int* pIntAry = new int[10];
char* pStr = new char[100];
MemSetName((pStr, "String"));
}

// 測試class
void TestClass()


{
Cls* pCls = new Cls();
}

// 測試釋放
void TestOk()


{
Cls* pCls = new Cls();
delete pCls;
pCls = NULL;
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)


{
int* pIntAry = new int[100];
return 0;
}

// 測試多線程
void TestMultiThread()


{
HANDLE hHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
WaitForSingleObject(hHandle, -1);
}

int main(int argc, char* argv[])


{
_DbgDllProcessAttach();

_afxGlobalData._hProcessHeap = GetProcessHeap();

TestBuiltin();
TestClass();
TestMultiThread();
TestOk();

_DbgDllProcessDetach();
return 0;
}
調(diào)試輸出窗口結(jié)果:
A + 4 - 0 = [ 4]
A + 40 - 0 = [ 44]
A + 100 - 0 = [ 144]
A + 8 - 0 = [ 152]
A + 400 - 0 = [ 552]
The thread 0x1D38 has exited with code 0 (0x0).
A + 8 - 0 = [ 560]
F + 0 - 8 = [ 552]
---------- Leaked Memory Blocks ----------
p=0x00144354 cb=400 #=4 TID:0x1d38
p=0x00144294 cb=8 #=3 TID:0x1878
p=0x001441a4 cb=100 #=2 TID:0x1878 String
p=0x001440ec cb=40 #=1 TID:0x1878
p=0x00142a54 cb=4 #=0 TID:0x1878
total size 552, peak size 560
---------- Leaked Memory Blocks End ------
其中A表示分配 F表示釋放
該工具本人初試沒有中毒癥狀,打算納入個人小寶庫中,希望大家喜歡!
下載。
posted on 2009-06-03 16:16
萬連文 閱讀(2800)
評論(11) 編輯 收藏 引用 所屬分類:
亂七八糟