中有bug”的分析"/>
VC中“其原因可能是堆被损坏,这也说明*.exe中或它所加载的任何DLL中有bug”的分析
找了一下午错误原因,整理一下大神的资料
原因分析
来自百度空间:DLL和exe里的malloc和free不能混用[hi.baidu/huhejun/item/988e41361d0a95c7392ffab5];
来自CSDN论坛:Dll分配的内存块,应用释放的问题;
来自CSDN博文:VC运行时库/MD、/MDd、/MT、/MTd说明 - holybin的专栏。
综合以上几个地方的讨论,给出一个总结分析:
1、首先是模块和进程的关系。win32环境下,模块分为两种:进程内模块和进程外模块,前者共享进程的内存空间,比如许多在进程中加载的dll;后者与进程一样,独立运行,通常供其它进程调用(由引用计数之类的管理),进程外模块当然也包括dll。在后面的叙述中简化成进程(exe)和同等地位的模块(dll)。【至于进程与模块的详细关系,我整理了一篇:模块与进程的关系 - holybin的专栏】
2、其次是在内存使用上,进程有堆,而线程有栈:一个进程(模块)一个堆,一个线程一个栈。由于一个进程必然有一个主线程,所以一个进程(模块)会有一个堆(用于new分配内存)和一个栈(用于局部变量)与之绑定,这两个的大小都可以通过编译器选项指定。所以exe和dll应该拥有不同的堆内存,使得在其中一个那里申请的内存,不能在另一个那里释放(在哪借东西就在哪里还对吧)。
3、最后最重要的是,C运行时库(Cruntime library,CRT)是静态连接的。CRT不是使用进程缺省的堆来实现malloc(new中调用malloc)的,而是使用一个全局句柄 HANDLE _crtheap来分配内存的,这个_crtheap是在XXXCRTStartUp(CRT提供的进口点函数)中创建的。 由于CRT静态连接,所以dll里有也会有CRT,因此也会有_crtheap。而在dll中的new(malloc)使用dll中的_crtheap句柄分配堆内存,在exe中的delete(free)中使用exe中的_crtheap释放堆内存,所以失败。
解决办法
1、使用单一的堆:分配内存把相应的new type[size]改为 HeapAlloc(GetProcessHeap(),0,size),释放内存时把相应的delete []p 改为HeapFree(GetProcessHeap(),0,p);或者用GlobalAlloc()代替new, 用GlobalFree()代替delete。
2、dll分配的内存由dll释放:在dll中输出一个函数给exe调用,专门用来释放由dll分配的内存。
综合(1)(2)两点,内存操作时不能混用malloc、new、free、delete等操作符和函数:
C
C++
Windows平台
COM
IMalloc接口
BSTR
申请
malloc()
new
GlobalAlloc()
CoTaskMemAlloc()
Alloc()
SysAllocString()
重新申请
realloc()
GlobalReAlloc()
CoTaskRealloc()
Realloc()
SysReAllocString()
释放
free()
delete
GlobalFree()
CoTaskMemFree()
Free()
SysFreeString()
以上这些函数必须要按类型配合使用(比如:new 申请的内存,则必须用 delete 释放;malloc申请的必须用free释放)。在 COM 内部,当然你可以随便使用任何类型的内存分配释放函数,但组件如果需要与客户进行内存的交互,则必须使用上表中的后三类函数族。IMalloc 接口又是对CoTaskXXX() 函数族的一个包装。包装后,同时增强了一些功能,比如:IMalloc::GetSize()可以取得尺寸,使用 IMallocSpy 可以监视内存的使用。
3、最简单的办法是修改项目属性中的“C运行时库”选项:项目属性—>配置属性—>C/C++—>代码生成—>运行时库,将Debug模式下的改成“多线程调试DLL(/MDd)”,或者将Release模式下的改成“多线程DLL(/MD)”。这样exe和dll就是使用同一个CRT(MSVCRT.DLL),就可以直接使用new和delete了。
再次编译运行时,可能会出现如下错误:fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD,表明需要设置共享DLL。
解决方案:“项目属性”—>“配置属性”—>“常规”—>“项目默认值”—>“MFC的使用”,选择“在共享 DLL 中使用 MFC”。
实际测试(出处不明)
测试1:使用malloc/free组合来分配和释放内存,DLL中使用 malloc分配,exe中使用free释放。
我建的是Win32 DLL工程, C/C++->Code generation 设置是 Multithread DLL debug, 但是exe工程设置是
MultiThread debug,所以不管怎么样,总是会抛异常. 这就间接证明了上述的描述是正确的, 若我修改exe工程设置是
MultiThread DLL debug, 那么malloc/free组合就能很好的工作起来了。
测试2:使用HeapAlloc/HeapFree组合来分配和释放内存,DLL 中使用HeapAlloc分配,exe中释放。
exe的配置还是MultiThread Debug,DLL中HeapAlloc(GetProcessheap(), HEAP_ZERO_MEMORY, 1024)分配,
exe中HeapFree(GetProcessHeap(), 0, p)释放,,则还是无法正常运行,还是抛异常。若exe中设置成MultiThread
DLL debug就正常运行了。
测试3:还是使用HeapAlloc/HeapFree来进行,但是DLL中导出一个方法来释放DLL中分配的内存。
若exe配置是MultiThread Debug,无法正常运行,抛异常。若修改成MultiThread DLL debug正常运行。
结论如下:
不管是使用malloc/free组合还是HeapAlloc/HeapFree组合,exe工程均需要设置成MultiThread DLL debug才能
正常运行起来的,CSDN上的那个讨论在这儿貌似是由出入的,而且DLL的设置不能随意修改。所以若有涉及到这种
问题的,最好的办法还是在哪个模块分配的就在哪个模块释放最好,要不然反倒会引来更多的麻烦。
---------------------
作者:holybin
来源:CSDN
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!
更多推荐
VC中“其原因可能是堆被损坏,这也说明*.exe中或它所加载的任何DLL中有bug”的分析
发布评论