原文链接:http://www.titilima.cn/?action=show&id=273
BoundsChecker是一个功能很强大的内存/资源泄漏检查工具,尤其是可以嵌入Visual Studio的环境中随着调试器一同运行,使用起来尤其方便。当程序退出后,BoundsChecker则会弹出一个视图,其中记录了代码中发生的内存泄漏。
不过,如果我们在代码中对内存分配做了特殊的处理,就有可能导致BoundsChecker的误报。考虑如下代码:
- #include<Windows.h>
- void*operatornew(size_tsize)
- {
- staticBYTEpool[4096];
- returnpool;
- }
- intmain(void)
- {
- int*p=newint[10];
- return0;
- }
在这个程序结束后,BoundsChecker就会报告有40字节的内存泄漏。很显然,事实上并非如此。
BoundsChecker的内存泄漏检查原理是挂钩了所有的内存/资源分配函数和销毁函数,当内存/资源分配时,则跳转到BoundsChecker自己的函数中,记录内存块的地址或资源句柄;当销毁内存/资源的时候,BoundsChecker则会删除对应的分配记录。这样一来,在程序退出后,仍然遗留下来的分配记录就是内存/资源的泄漏了。
对于上面的源代码,当启用BoundsChecker调试时,operator new的反汇编代码如下:
- 3:void*operatornew(size_tsize)
- 4:{
- 00401020jmp01172348
- 00401025inceax
- 00401026pushebx
- 00401027pushesi
- 00401028pushedi
- 00401029leaedi,[ebp-40h]
- 0040102Cmovecx,10h
- 00401031moveax,0CCCCCCCCh
- 00401036repstosdwordptr[edi]
- 5:staticBYTEpool[4096];
- 6:returnpool;
- 00401038moveax,offsetpool(00432a78)
- 7:}
- 0040103Dpopedi
- 0040103Epopesi
- 0040103Fpopebx
- 00401040movesp,ebp
- 00401042popebp
- 00401043ret
如你所见,01172348这个地址就是BoundsChecker的内存统计函数了。也正因为BoundsChecker只是简单收集了内存块的大小而并没有关心程序员是如何实现内存分配的,所以这里的误报就在所难免了。其实在某种意义上来说,即使是使用了自己的内存池,如果分配完后不释放也可以算作内存泄漏——因为这个行为造成了内存池的浪费。
但是上面这种重载new倒是会造成类似Rational Purify的工具进行错误的内存统计,因为所有的内存都在内存池中一次性分配好了。
话又说回来——我为什么要故意和BoundsChecker对着干呢?