码迷,mamicode.com
首页 > Windows程序 > 详细

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

时间:2016-04-18 06:31:20      阅读:480      评论:0      收藏:0      [点我收藏+]

标签:

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

c/C++的一个让人疯狂的地方就是内存管理,非法访问、越界、野指针、泄漏、内存分配器等诸多问题,有时候一个编程老手也会迷惘困惑。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内存分配工具

标签:

原文地址:http://www.cnblogs.com/findumars/p/5403036.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!