现在的位置: 首页 > 综合 > 正文

VC中“其原因可能是堆被损坏,这也说明*.exe中或它所加载的任何DLL中有bug”的分析

2014年09月05日 ⁄ 综合 ⁄ 共 2726字 ⁄ 字号 评论关闭

网上的讨论很多,这里整理总结一下存档备份。

原因分析

来自百度空间:DLL和exe里的malloc和free不能混用[hi.baidu.com/huhejun/item/988e41361d0a95c7392ffab5];

来自CSDN论坛:Dll分配的内存块,应用释放的问题

来自CSDN博文:VC运行时库/MD、/MDd、/MT、/MTd说明 - holybin的专栏

综合以上几个地方的讨论,给出一个总结分析:

1、首先是模块和进程的关系win32环境下,模块分为两种:进程内模块和进程外模块,前者共享进程的内存空间,比如许多在进程中加载的dll;后者与进程一样,独立运行,通常供其它进程调用(由引用计数之类的管理),进程外模块当然也包括dll。在后面的叙述中简化成进程(exe)和同等地位的模块(dll)。【至于进程与模块的详细关系,我整理了一篇:模块与进程的关系
- holybin的专栏

2、其次是在内存使用上,进程有堆,而线程有栈:一个进程(模块)一个堆,一个线程一个栈。由于一个进程必然有一个主线程,所以一个进程(模块)会有一个堆(用于new分配内存)和一个栈(用于局部变量)与之绑定,这两个的大小都可以通过编译器选项指定。所以exedll应该拥有不同的堆内存,使得在其中一个那里申请的内存,不能在另一个那里释放(在哪借东西就在哪里还对吧)。

3、最后最重要的是,C运行时库(Cruntime libraryCRT是静态连接的。CRT不是使用进程缺省的堆来实现mallocnew中调用malloc)的,而是使用一个全局句柄
HANDLE _crtheap
来分配内存的,这个_crtheap是在XXXCRTStartUpCRT提供的进口点函数)中创建的。 由于CRT静态连接,所以dll里有也会有CRT,因此也会有_crtheap。而在dll中的newmalloc)使用dll中的_crtheap句柄分配堆内存,在exe中的deletefree)中使用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的设置不能随意修改。所以若有涉及到这种

问题的,最好的办法还是在哪个模块分配的就在哪个模块释放最好,要不然反倒会引来更多的麻烦。


抱歉!评论已关闭.