windows下vc进行内存泄露终结版



1、在mai函数中增加了_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF);

在debug下程序退出时,输出会打印很多内存泄露情况。 我们根据这个特性,原理可以实现更强大的代码级检测机制。

_CRTDBG_LEAK_CHECK_DF标记的含义比较简单,就是在程序退出时调用函数_CrtDumpMemoryLeaks,该函数从内存分配的队列里面获取,

还有哪些内存没有释放。

UMDH的实现原理:在某个时间点开启抓取一段当前内存分配情况,在另一个时间段停止抓取内存分配情况。然后对比,stop-start的就是泄露的内存了。

umdh是测试期,利用gflag来跟踪堆的分配情况,很多不是源代码级的,看起来比较费劲,如果能在开发阶段解决内存泄露成本是最低的

 

2、每个内存new出来后,在内存上方都会多一个结构体,来跟踪记录这段内存(可以尝试连续两次new,查看两个new的地址差),这个结构体就是_CrtMemBlockHeader(release下不会分配)

typedef struct _CrtMemBlockHeader
{
        struct _CrtMemBlockHeader * pBlockHeaderNext; // 下一个new的位置
        struct _CrtMemBlockHeader * pBlockHeaderPrev;// 上一个new的 位置
        char *                      szFileName; // 调用new的文件名
        int                         nLine;// 调用new的行号
#ifdef _WIN64
        /* These items are reversed on Win64 to eliminate gaps in the struct
         * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
         * maintained in the debug heap.
         */
        int                         nBlockUse;
        size_t                      nDataSize;
#else  /* _WIN64 */
        size_t                      nDataSize; // 分配的内存大小
        int                         nBlockUse; // 块类型,用户态,内核态等,我们只需要关心_NORMAL_BLOCK的类型块就好
#endif  /* _WIN64 */
        long                        lRequest; // 块号,每块内存的id号
        unsigned char               gap[nNoMansLandSize];
        /* followed by:
         *  unsigned char           data[nDataSize];
         *  unsigned char           anotherGap[nNoMansLandSize];
         */
} _CrtMemBlockHeader;

windows下会记录这个链表,首地址是个全局变量

static _CrtMemBlockHeader * _pFirstBlock;

它还有几个很好的函数可供使用

typedef struct _CrtMemState
{
        struct _CrtMemBlockHeader * pBlockHeader;
        size_t lCounts[_MAX_BLOCKS]; // new的数量
        size_t lSizes[_MAX_BLOCKS];// new的大小,_MAX_BLOCKS是块类型,用户分配的块类型是_NORMAL_BLOCK
        size_t lHighWaterCount;
        size_t lTotalCount;
} _CrtMemState;
_CrtMemState current_mem_state;
_CrtMemCheckpoint(&current_mem_state); // 保存当前的内存链表

 

3、有了2这些信息,再实现内存泄露检测就显得非常简单了

在认为可以开始检测的时候调用一次_CrtMemCheckpoint(&start_mem_state);

在运行一段时间后调用一次_CrtMemCheckpoint(&stop_mem_state);

比较两个链表,如果stop有,但是start没有,就是新分配的_CrtMemBlockHeader了,打印出文件信息,行号就可以了。

 

4、上面看似已经没有问题了,但是,但是效果真的可以吗?

实际操作后会发现_CrtMemBlockHeader中的两个值是空!!!

        char *                      szFileName; // 调用new的文件名
        int                         nLine;// 调用new的行号

这也是为什么步骤1描述的程序退出时打印了很多内存泄露,但是只有一个块号,大小,这个我们是查不到原因的。

 Detected memory leaks!
Dumping objects ->
{145} normal block at 0x006FD878, 95832 bytes long. // 145就是块号,normal block 是类型
 Data: <              @ > CD CD CD CD 01 00 00 00 00 00 00 00 00 00 40 00
Object dump complete.

回过来想想也对,new的时, _CrtMemBlockHeader怎么会知道你的文件信息,行号了。没有关系这个问题也有解决办法

#ifdef _DEBUG
#define DBG_NEW_UTIL_TEST new (_NORMAL_BLOCK, __FILE__, __LINE__)
#else
#define DBG_NEW_UTIL_TEST new // release下继续使用默认的
#endif

windows提供了另外一种new的方式,参数增加了行号,列号。该new的原型在dbgnew.cpp文件中

void *__CRTDECL operator new(
        size_t cb,
        int nBlockUse,
        const char * szFileName,
        int nLine
        )

在我们的代码中后续将new修改为DBG_NEW_UTIL_TEST即可了

 

5、结束语:原理已经讲完,源码就不提供了,可以根据上面原理,自己实现。

另外还有几个与此相关的几个函数,网上可以查到它们的帮助文档

_CrtMemDifference

_CrtMemDumpAllObjectsSince

_CrtSetBreakAlloc

_CrtSetAllocHook

_CrtSetDbgFlag

 

参考书目<<visual c++.net 运行库函数大全>>,msdn帮助

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值