首页 > 代码库 > [转载]DLL和exe里的malloc和free不能混用的问题
[转载]DLL和exe里的malloc和free不能混用的问题
源自:
http://bjwf.cndev.org/2004/06/03/559/
http://bbs.csdn.net/topics/40214261
======================================
今天老玉米提了一个问题问为什么dll里malloc的内存如果在exe里free的话会出错,我分析了一下C库的原代码,得出结论如下:
刚看了一下malloc和free 的源代码,在这两个函数中都有对全局变量的引用,而malloc和free是C库函数,分别被静态链接到exe和dll里,这样他们引用的全局变量也会有两份各是各的,自然不能混用。
GlobalAlloc之类的windows API函数应该行。
茶壶贴了一贴,说dll因为有自已的堆栈所以不能混用,我表示怀疑,继续看源代码,找到支持上面结论的真正原因:
DLL是否有自已的堆栈我不知道, 但dll和exe里的malloc和free不能混用的真正原因就是我上面说的,有源代码为证,
malloc 里真正分配的是这一句,见malloc.c里
return HeapAlloc( _crtheap, 0, size );
_crtheap是个全局变量,在_heap_init这个函数里初始化,见heapinit.c:
// Initialize the “big-block” heap first.
if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL )
return 0;
而_heap_init 在wWinMainCRTStartup 这个函数里调用(见crt0.c),同时也会在_DllMainCRTStartup里调用(见dllcrt0.c )。而winmain和dllmain 分别在wWinMainCRTStartup 和DllMainCRTStartup里调用,但都在调用了_heap_init之后(事实上是DllMainCRTStartup调用了_CRT_INIT之后才会调用用户的dllmain,而_CRT_INIT里调用了_heap_init这也就是说至少_crtheap这个变量在dll和exe里会各有一份,这才是dll和exe里不能混用malloc和free的真正原因。
如果使用LoadLibraryEx装入dll,并指定DONT_RESOLVE_DLL_REFERENCES参数来让系统不调用用户的dllmain,dll和exe的malloc和free仍然不能混用,因为即使指定了DONT_RESOLVE_DLL_REFERENCES参数DllMainCRTStartup里仍会调用_CRT_INIT来初始化自已的_crtheap。
当然如果dll和exe的编写语言不同的话,更不能混用了,各种语言都会封装winAPI来实现自已的内存管理库函数。
DLL中的内存分两类,一类是共享内存,一类是进程私有内存
例如,进程P1和P2同时引用同一个DLL,一般情况下,DLL中声明的变量会在P1和P2的进程空间内产生各自的副本
也就是说,DLL中的变量,对于各个进程是独立的
某些情况下,有必要在DLL中产生一块共享内存,多个进程都访问到同一块数据,比如,某些设备驱动程序,需要内部保留一个设备忙/空闲之类的状态标志,这个标志就必须作为共享内存存在,保证只有一份
对于DLL中分配的内存,我觉得本身没有什么神秘之处,Windows程序之下的内存分配,最终都需要通过OS来完成,老玉米的问题,很可能是象北京色狼说的那样,不是内存本身的问题,是内存管理程序的故障
因为各种语言的RTL库中的内存管理程序,它本身也需要通过自己的内部数据来管理内存分配的状态
在DLL中调用的内存管理程序和在EXE中调用的内存管理程序不是同一块,而且它们内部维护内存分配信息的数据也不是同一块,所以造成老玉米的问题
如果上面的说法成立的话,那么,不要用C的RTL来申请和释放内存,直接通过Windows API来申请和释放内存,比如GlobalAlloc/GlobalFree/LocalAlloc/LocalFree/HeapAlloc/HeapFree等等,应该就不会有问题了
====================================================
一个模块一个堆,一个线程一个栈。
问题的一切都要归咎于C运行时期库是静态连接的。
我回去看了一下C运行时期库的源码,发现一个很搞笑的事实:CRT(C运行时期库)不是使用进程缺省的堆来实现malloc(new中调用malloc)的,而是使用一个全局句柄HANDLE __crtheap(好象是_crtheap,记不清了)来分配内存的。这个_crtheap是在XXXCRTStartUp(CRT提供的进口点函数)中创建的。
由于CRT静态连接,则楼主的DLL里有也有一个CRT,因此也有一个_crtheap。而在dll中的new使用dll中的_crtheap句柄分配堆,在exe中的delete使用exe中的_crtheap释放堆,当然失败!不信的话,楼主大可在你的exe和dll中各下一个断点,在变量查看中输入_crtheap来查看其值,一般会差个0x10000000-0x04000000,因为缺省dll的基址是0x10000000,而exe的是0x04000000。
因此你的模块也有一个与之相关联的堆了,因为使用C/C++语言编写,而且CRT静态连接
[转载]DLL和exe里的malloc和free不能混用的问题