技術迴歸01-Windows記憶體配置工具

來源:互聯網
上載者:User

標籤:

很久沒有寫技術方面的東西了,這半年主要是在學習別人的東西,對自己提高比較大,算是一次技術回籠吧,這次學習之旅目的是結束技術方面的專註,開始嚮應用方面找突破口,也就是完成技術積累或者為技術的積累做堅實的準備。

c/C++的一個讓人瘋狂的地方就是記憶體管理,非法訪問、越界、野指標、泄漏、記憶體 Clerk等諸多問題,有時候一個編程老手也會迷惘困惑。Crt有一些堆棧檢查的函數可以完成基本的記憶體狀況檢查,MFC也有一些簡單的對象檢查機制,當然好的算是java、.net等sdk的超重量級封裝了,即使發生對象錯誤也能把堆棧資訊明明白白的告訴你(至少表面上是這樣,具體我對這兩種語言沒有做過開發)。下面介紹的是某牛公司實現的記憶體配置工具,基本實現了記憶體流失檢查,對象合法性檢查,對於我來說已經夠用了。


為了對記憶體配置塊進行跟蹤,設計如下結構體:

//+--------------------------------------------------------------
//
// 每個請求分配記憶體塊的首碼結構體
// 用來跟蹤所有請求分配塊以及請求分配名稱
//
//---------------------------------------------------------------
struct DBGALLOCHDR
{
    DBGALLOCHDR*    pdbgahPrev; // 前一個記憶體塊頭
    DBGALLOCHDR*    pdbgahNext; // 後一個記憶體塊頭
    DWORD           iAllocated; // 記錄是第幾次請求分配操作
    DWORD           tid;        // 請求分配線程的ID
    size_t          cbRequest;  // 請求分配大小
    char            szName[64]; // 請求分配塊名稱
    DWORD           adwGuard[4];// 保護頭
};

//+--------------------------------------------------------------
//
// 每個請求分配記憶體塊的尾碼結構體
// 使用特定的資料填充用來檢測指標是合法
//
//---------------------------------------------------------------
struct DBGALLOCFOOT
{
    DWORD adwGuard[4];
};

// 記憶體跟蹤塊的根,通過根可以擷取所有分配塊
DBGALLOCHDR g_dbgahRoot =
{
    &g_dbgahRoot,
    &g_dbgahRoot,
    0,
    (DWORD)-1
};



為了實現多線程記憶體配置跟蹤,採用Tls技術使用線程局部對象儲存當前分配資訊:

// 線程局部對象結構體,輔助實現每個線程的請求記憶體配置記錄
struct DBGTHREADSTATE
{
    DBGTHREADSTATE* ptsNext;
    DBGTHREADSTATE* ptsPrev;

    // Add globals below
    void*           pvRequest;  // 線程最後一次請求分配記憶體的指標
    size_t          cbRequest;  // 線程最後一次請求分配記憶體的大小
};



// 調試期間實際分配記憶體大小=請求分配+分配頭+分配尾
size_t _ActualSizeFromRequestSize(size_t cb)
{
    return cb+sizeof(DBGALLOCHDR)+sizeof(DBGALLOCFOOT);
}



主要實現的記憶體配置工具有如下這些:

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)



通過宏實作類別的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); }



在應用的時候可以重寫全域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); }


使用注意:
進程啟動時候需要調用:
_DbgDllProcessAttach();
_afxGlobalData._hProcessHeap = GetProcessHeap();

進程退出的時候需要調用:
_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;
}


調試輸出視窗結果:
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表示釋放

 

該工具本人初試沒有中毒癥狀,打算納入個人小寶庫中,希望大家喜歡!

下載。

http://www.cppblog.com/wlwlxj/archive/2009/06/03/86660.html

技術迴歸01-Windows記憶體配置工具

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.