Page Heap机制

来源:互联网 发布:java 内存流 生成pdf 编辑:程序博客网 时间:2024/04/28 04:23
转---Page Heap机制
2009-05-05 21:11

提示说堆内存被破坏,有时候这样的错误在比较小的程序里面也许不会对整个程序造成破坏,依然能够继续运行,但是千万不要放过,像这种破坏堆内存的隐藏BUG,说不准哪天就会造成整个软件的crash。另外我要提醒的是,release版本也许什么提示都没有,直接放过了,这是因为在debug下,操作系统用DebugWin32Heap来代替正常的heap分配内存空间。在这个堆上的任何操作,debug的堆管理器会检查堆的数据完整性,如果它发现了一个错误,就会报告一个消息上来。

我们可以对这种情况作一个猜测,既然是delete的时候出了问题,那就是这个程序很可能去访问了“非法”的内存,这里的非法内存是指不是由你的程序分配的内存块,但是被你的程序在某种情况下访问到了,当然这是堆上的情况,所以在release下可能一时不会出问题,如果是栈上,程序也许早就是crase或者出现莫名其妙的错误了。

现在问题比较集中了,但是整个程序在堆上分配了那么多对象,到底哪次分配出了问题?这还是很难定位到错误,用BoundsChecker完整跑一遍是一个好办法,但是比较麻烦,其实微软已经给我们想到了办法,试想如果在每次分配的内存块边界做限制,设置为虚拟内存,也就是NO_ACCESS(不可访问),那程序试图读写这个地方的时候,就会出错,程序会马上断下来,也就是所谓的PageHeap机制。

要让我们的程序启用Full Page Heap机制,需在注册表中
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Image File Execution Options/appName.exe下增加如下设置,

属性名:GlobalFlag,字符串类型,值:x02200000

属性名:PageHeapFlags,字符串类型,值:0x3

属性名:VerifierFlags,DWORD类型,值:1

另外替换appName.exe为你正在调试的真正的程序文件名,如果你安装了Debugging Tools for Windows,这一切会更加简单,在安装目录下有个gflags工具,直接用命令行运行 gflags –p /enable appName.exe /full 即可自动帮你添加上述注册表值,如果你没有该工具,你可以去http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx下载。

http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx#b

DLL也是可以用这个机制的,注册表的设置有点区别,如下:

GlobalFlag,字符串类型,值是0x02000000

PageHeapTargetDlls,字符串类型,值是调试的dll名称,不带路径

VerifierFlags,DWORD类型,值是00000001

PageHeapFlags,字符串类型,0x403

 

或者使用命令行 gflags –p /enable appName.exe /full /dlls dllName.dll 这里要注意的是,注册表的子健值应该是dll依附的exe程序名。

好,使用该机制后再次调试,果然断下来了,在一个对象里面的结构体里面出错了,这个结构体是该类的最后一个成员,该结构体的最后四个DWORD的值显示是无效的,把这个结构体放在前面后,这个类的最后四个DWORD大小的空间还是无效的,现在可以知道,分配内存的时候,根本没有给该对象分配足够的内存,而delete的时候却按该对象的大小来释放,自然就被d堆管理器捕获了这个错误,为什么会少分配4个dword的大小?后来发现是滥用内联函数造成的,构造和析构函数使用了内联,其实内联这个东东,编译器完全可以无视,也就是说不给你内联,但是也许又给你内联,怎么理解?也就是说内联不内联完全由编译器决定,而程序员无法控制,这在win32里面有个词叫什么 - 不可预测,呵呵,既然是强大的不可预测,就不要为了那么一点点效率来使用内联了,后来把构造析构函数搬到CPP文件里面之后,分配马上就正常了。

 

重现上面的bug其实很容易,如下:

// 演示一个BUG

char* pHeap = new char[2];

::lstrcpy(pHeap, "tonglei");

delete pHeap;

原创粉丝点击