• 谁分配谁释放HEAP


    在linux下跑得一直很好的程序,到了windows下面就跑不起来了。内存异常,检查了一下,很快发现是因为在主程序中释放了一块在DLL中分配的内存,这种问题虽然早就知道了,但是一直没有仔细考虑过,所以今天就深入研究了一下。



    在linux下,每个进程只有一个heap,在任何一个动态库模块so中通过new或者malloc来分配内存的时候都是从这个唯一的heap中分配的,那么自然你在其它随便什么地方都可以释放。这个模型是简单的。

    但是在windows下面,问题变得复杂了。

    1、windows允许一个进程中有多个heap,那么这样就需要指明一块内存要在哪个heap上分配,win32的HeapAlloc函数就是这样设计的,给出一个heap的句柄,给出一个size,然后返回一个指针。每个进程都至少有一个主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(), size)来分配的内存是可以在dll以外释放的,因为这时内存分配在全局的主heap上,而不是分配在dll自己的__crtheap上)

  • 相关阅读:
    cmake默认变量
    关于字符编码
    cmake 变量
    动态库对外暴露api的方法
    vs2013工程技巧
    大学毕业4年-未来展望(1)-商业研究
    大学毕业4年-未来展望-序
    大学毕业4年-未来展望-序
    大学毕业4年-回顾和总结(3)-投资理财观-图穷而真相现
    大学毕业4年-回顾和总结(3)-投资理财观-图穷而真相现
  • 原文地址:https://www.cnblogs.com/hgy413/p/3693604.html
Copyright © 2020-2023  润新知