很久没有写技术方面的东西了,这半年主要是在学习别人的东西,对自己提高比较大,算是一次技术回笼吧,这次学习之旅目的是结束技术方面的专注,开始向应用方面找突破口,也就是完成技术积累或者为技术的积累做坚实的准备。

c/C++的一个让人疯狂的地方就是内存管理,非法访问、越界、野指针、泄漏、内存分配器等诸多问题,有时候一个编程老手也会迷惘困惑。Crt有一些堆栈检查的函数可以完成基本的内存状况检查,MFC也有一些简单的对象检查机制,当然好的算是java、.net等sdk的超重量级封装了,即使发生对象错误也能把堆栈信息明明白白的告诉你(至少表面上是这样,具体我对这两种语言没有做过开发)。下面介绍的是某牛公司实现的内存分配工具,基本实现了内存泄漏检查,对象合法性检查,对于我来说已经够用了。

为了对内存分配块进行跟踪,设计如下结构体:


技术回归01-Windows内存分配工具_多线程//+--------------------------------------------------------------

技术回归01-Windows内存分配工具_多线程//

技术回归01-Windows内存分配工具_多线程// 每个请求分配内存块的前缀结构体

技术回归01-Windows内存分配工具_多线程// 用来跟踪所有请求分配块以及请求分配名称

技术回归01-Windows内存分配工具_多线程//

技术回归01-Windows内存分配工具_多线程//---------------------------------------------------------------

技术回归01-Windows内存分配工具_多线程struct DBGALLOCHDR

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    DBGALLOCHDR*    pdbgahPrev; // 前一个内存块头

技术回归01-Windows内存分配工具_内存分配_09    DBGALLOCHDR*    pdbgahNext; // 后一个内存块头

技术回归01-Windows内存分配工具_内存分配_09    DWORD           iAllocated; // 记录是第几次请求分配操作

技术回归01-Windows内存分配工具_内存分配_09    DWORD           tid;        // 请求分配线程的ID

技术回归01-Windows内存分配工具_内存分配_09    size_t          cbRequest;  // 请求分配大小

技术回归01-Windows内存分配工具_内存分配_09    char            szName[64]; // 请求分配块名称

技术回归01-Windows内存分配工具_内存分配_09    DWORD           adwGuard[4];// 保护头

技术回归01-Windows内存分配工具_#define_16};

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程//+--------------------------------------------------------------

技术回归01-Windows内存分配工具_多线程//

技术回归01-Windows内存分配工具_多线程// 每个请求分配内存块的后缀结构体

技术回归01-Windows内存分配工具_多线程// 使用特定的数据填充用来检测指针是合法

技术回归01-Windows内存分配工具_多线程//

技术回归01-Windows内存分配工具_多线程//---------------------------------------------------------------

技术回归01-Windows内存分配工具_多线程struct DBGALLOCFOOT

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    DWORD adwGuard[4];

技术回归01-Windows内存分配工具_#define_16};

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程// 内存跟踪块的根,通过根可以获取所有分配块

技术回归01-Windows内存分配工具_多线程DBGALLOCHDR g_dbgahRoot =

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    &g_dbgahRoot,

技术回归01-Windows内存分配工具_内存分配_09    &g_dbgahRoot,

技术回归01-Windows内存分配工具_内存分配_09    0,

技术回归01-Windows内存分配工具_内存分配_09    (DWORD)-1

技术回归01-Windows内存分配工具_#define_16};



为了实现多线程内存分配跟踪,采用Tls技术使用线程局部对象保存当前分配信息:


技术回归01-Windows内存分配工具_多线程// 线程局部对象结构体,辅助实现每个线程的请求内存分配记录

技术回归01-Windows内存分配工具_多线程struct DBGTHREADSTATE

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    DBGTHREADSTATE* ptsNext;

技术回归01-Windows内存分配工具_内存分配_09    DBGTHREADSTATE* ptsPrev;

技术回归01-Windows内存分配工具_内存分配_09

技术回归01-Windows内存分配工具_内存分配_09    // Add globals below

技术回归01-Windows内存分配工具_内存分配_09    void*           pvRequest;  // 线程最后一次请求分配内存的指针

技术回归01-Windows内存分配工具_内存分配_09    size_t          cbRequest;  // 线程最后一次请求分配内存的大小

技术回归01-Windows内存分配工具_#define_16};

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程// 调试期间实际分配内存大小=请求分配+分配头+分配尾

技术回归01-Windows内存分配工具_多线程size_t _ActualSizeFromRequestSize(size_t cb)

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    return cb+sizeof(DBGALLOCHDR)+sizeof(DBGALLOCFOOT);

技术回归01-Windows内存分配工具_#define_16}



主要实现的内存分配工具有如下这些:


技术回归01-Windows内存分配工具_多线程void*   _MemAlloc(ULONG cb);

技术回归01-Windows内存分配工具_多线程void*   _MemAllocClear(ULONG cb);

技术回归01-Windows内存分配工具_多线程HRESULT _MemRealloc(void** ppv, ULONG cb);

技术回归01-Windows内存分配工具_多线程ULONG   _MemGetSize(void* pv);

技术回归01-Windows内存分配工具_多线程void    _MemFree(void* pv);

技术回归01-Windows内存分配工具_多线程HRESULT _MemAllocString(LPCTSTR pchSrc, LPTSTR* ppchDst);

技术回归01-Windows内存分配工具_多线程HRESULT _MemAllocString(ULONG cch, LPCTSTR pchSrc, LPTSTR* ppchDst);

技术回归01-Windows内存分配工具_多线程HRESULT _MemReplaceString(LPCTSTR pchSrc, LPTSTR* ppchDest);

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程#define MemAlloc(cb)                            _MemAlloc(cb)

技术回归01-Windows内存分配工具_多线程#define MemAllocClear(cb)                       _MemAllocClear(cb)

技术回归01-Windows内存分配工具_多线程#define MemRealloc(ppv, cb)                     _MemRealloc(ppv, cb)

技术回归01-Windows内存分配工具_多线程#define MemGetSize(pv)                          _MemGetSize(pv)

技术回归01-Windows内存分配工具_多线程#define MemFree(pv)                            _MemFree(pv)

技术回归01-Windows内存分配工具_多线程#define MemAllocString(pch, ppch)               _MemAllocString(pch, ppch)

技术回归01-Windows内存分配工具_多线程#define MemAllocStringBuffer(cch, pch, ppch)    _MemAllocString(cch, pch, ppch)

技术回归01-Windows内存分配工具_多线程#define MemReplaceString(pch, ppch)             _MemReplaceString(pch, ppch)

技术回归01-Windows内存分配工具_多线程#define MemFreeString(pch)                      _MemFree(pch)



通过宏实现类的new delete重写:


技术回归01-Windows内存分配工具_多线程#define DECLARE_MEMALLOC_NEW_DELETE() \

技术回归01-Windows内存分配工具_d3_08    inline void* __cdecl operator new(size_t cb)    { return(MemAlloc(cb)); } \

技术回归01-Windows内存分配工具_d3_08    inline void* __cdecl operator new[](size_t cb)  { return(MemAlloc(cb)); } \

技术回归01-Windows内存分配工具_d3_08    inline void __cdecl operator delete(void* pv)   { MemFree(pv); }

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程#define DECLARE_MEMCLEAR_NEW_DELETE() \

技术回归01-Windows内存分配工具_d3_08    inline void* __cdecl operator new(size_t cb)    { return(MemAllocClear(cb)); } \

技术回归01-Windows内存分配工具_d3_08    inline void* __cdecl operator new[](size_t cb)  { return(MemAllocClear(cb)); } \

技术回归01-Windows内存分配工具_d3_08    inline void __cdecl operator delete(void* pv)   { MemFree(pv); }



在应用的时候可以重写全局new delete:


技术回归01-Windows内存分配工具_多线程// 测试全局new delete

技术回归01-Windows内存分配工具_d3_08void* __cdecl operator new(size_t cb)    { return(MemAlloc(cb)); }

技术回归01-Windows内存分配工具_d3_08void* __cdecl operator new[](size_t cb)  { return(MemAlloc(cb)); }

技术回归01-Windows内存分配工具_d3_08void __cdecl operator delete(void* pv)   { MemFree(pv); }


使用注意:

进程启动时候需要调用:

_DbgDllProcessAttach();

_afxGlobalData._hProcessHeap = GetProcessHeap();

进程退出的时候需要调用:

_DbgDllProcessDetach();


测试用例:


技术回归01-Windows内存分配工具_多线程// 测试基本类型

技术回归01-Windows内存分配工具_多线程void TestBuiltin()

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    // 基本类型

技术回归01-Windows内存分配工具_内存分配_09    int* pInt = new int(10);

技术回归01-Windows内存分配工具_内存分配_09    int* pIntAry = new int[10];

技术回归01-Windows内存分配工具_内存分配_09    char* pStr = new char[100];

技术回归01-Windows内存分配工具_内存分配_09    MemSetName((pStr, "String"));

技术回归01-Windows内存分配工具_#define_16}

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程// 测试class

技术回归01-Windows内存分配工具_多线程void TestClass()

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    Cls* pCls = new Cls();

技术回归01-Windows内存分配工具_#define_16}

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程// 测试释放

技术回归01-Windows内存分配工具_多线程void TestOk()

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    Cls* pCls = new Cls();

技术回归01-Windows内存分配工具_内存分配_09    delete pCls;

技术回归01-Windows内存分配工具_内存分配_09    pCls = NULL;

技术回归01-Windows内存分配工具_#define_16}

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程DWORD WINAPI ThreadProc(LPVOID lpParameter)

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    int* pIntAry = new int[100];

技术回归01-Windows内存分配工具_内存分配_09    return 0;

技术回归01-Windows内存分配工具_#define_16}

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程// 测试多线程

技术回归01-Windows内存分配工具_多线程void TestMultiThread()

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    HANDLE hHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

技术回归01-Windows内存分配工具_内存分配_09    WaitForSingleObject(hHandle, -1);

技术回归01-Windows内存分配工具_#define_16}

技术回归01-Windows内存分配工具_多线程

技术回归01-Windows内存分配工具_多线程int main(int argc, char* argv[])

技术回归01-Windows内存分配工具_d3_08{

技术回归01-Windows内存分配工具_内存分配_09    _DbgDllProcessAttach();

技术回归01-Windows内存分配工具_内存分配_09

技术回归01-Windows内存分配工具_内存分配_09    _afxGlobalData._hProcessHeap = GetProcessHeap();

技术回归01-Windows内存分配工具_内存分配_09

技术回归01-Windows内存分配工具_内存分配_09    TestBuiltin();

技术回归01-Windows内存分配工具_内存分配_09    TestClass();

技术回归01-Windows内存分配工具_内存分配_09    TestMultiThread();

技术回归01-Windows内存分配工具_内存分配_09    TestOk();

技术回归01-Windows内存分配工具_内存分配_09

技术回归01-Windows内存分配工具_内存分配_09    _DbgDllProcessDetach();

技术回归01-Windows内存分配工具_内存分配_09    return 0;

技术回归01-Windows内存分配工具_#define_16}


调试输出窗口结果:

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表示释放

 

该工具本人初试没有中毒症状,打算纳入个人小宝库中,希望大家喜欢!

​下载​​。