【转载】Windows 下主程序与动态库(*.dll)释放对方分配的内存操作要点

声明:转载自Windows 下主程序与动态库(*.dll)释放对方分配的内存操作要点 同样的代码程序: 主程序中释放了一块在 动态库(*.dll)或共享库(*.so) 中分配的内存, Windows 将会出现程序崩溃,而 Linux 则正常运行。   在 linux 下,每个进程只有一个 heap , 在任何一个共享库模块 *.so 中通过 new 或者 malloc 来分配内存的时候都是从这个唯一的 heap 中分配的, 那么自然你在其它什么地方都可以释放。   但是 windows 下面确不是如此: 1. windows 允许一个进程中有多个 heap ,那么这样就需要指明一块内存要在哪个 heap 上分配,    win32 的 HeapAlloc 函数就是这样设计的,    给出一个 heap 的句柄、一个可选的分配操作标志、一个字节块大小,然后返回一个指针。    每个进程都至少有一个主 heap ,它的句柄可以通过 GetProcessHeap 来获得,    其它的堆句柄可以通过 GetProcessHeaps 取到。    同样,内存释放的时候通过 HeapFree 来完成,还是需要指定一个堆句柄。   2. 这样的设计显然是比较灵活的,   但是问题在于这样的话,每次分配内存的时候就必须要显式的指定一个 heap 句柄,    对于 crt 中的 new/malloc ,显然需要特殊处理。    那么如何处理就取决于 crt 的实现了。    VC++ 的 crt 是创建了一个单独的 heap,叫做 __crtheap ,它对于用户是看不见的,    但是在 new/malloc 的实现中,都是用 HeapAlloc 在这个 __crtheap 堆上分配的,    也就是说 malloc(size) 基本上可以认为等同于 HeapAlloc(__crtheap, size)    (当然实际上 crt 内部还要维护一些内存管理的数据结构,     所以并不是每次 malloc 都必然会触发 HeapAlloc ),    这样 new/malloc 就和 windows 的 heap 机制吻合了。    (这里说的是 VC 的 crt 实现,我不知道其它 crt 实现是否如此)   3. 如果一个进程需要动态库支持,系统在加载 dll 的时候,在 dll 的启动代码 _DllMainCRTStartup 中,    会创建这个 __crtheap ,所以理论上有多少个 dll,就有多少个 __crtheap 。    最后主进程的 mainCRTStartup 中还会创建一个为主进程服务的 __crtheap 。    (由于顺序总是先加载 dll ,然后才启动 main 进程,     所以你可以看到各个 dll 的 __crtheap 地址比较小,     而主进程的 __crtheap 地址比较大,当然排在最前面的堆是每个进程的主 heap 。)   4. 从上面的分析中可以看出,对于 crt 来说,由于每个 dll 都有自己的 heap ,    所以每个 dll 通过 new/malloc 分配的内存都是在自己 dll 内部的那个 heap 上用 HeapAlloc 来分配的,    而如果你想在其它模块中释放,那么在释放的时候 HeapFree 就会失败了,    因为各个模块的 __crtheap 是不一样的。   事情基本清楚了,在 windows 下一个进程存在着多个 heap , 除了一个主 heap 外,还有很多的 __crtheap ,用来处理通过 C/C++ 的运行库进行的内存操作。 所以使用 new/malloc 来分配的内存实际上都是局部的,可以在多个 dll *享, 但是却必须是谁申请谁释放。   这个是 Windows 下的一个规则。以前知道这个规则,但是不知道为什么,现在算是比较明白了。 如果在 dll 内部使用 HeapAlloc(GetProcessHeap(), 0, 字节块大小 ) 来分配的内存, 是可以在 dll 以外释放的, 因为这时内存分配在全局的主 heap 上,而不是分配在 dll 自己的 __crtheap 上。
上一篇:LibOpenCM3(三) .ld文件(连接器脚本)和startup代码说明


下一篇:git clone 下载时报错: fatal: Out of memory, malloc failed