C++ 卸载DLL时内存泄漏是否会导致主机进程泄漏?

C++ 卸载DLL时内存泄漏是否会导致主机进程泄漏?,c++,winapi,dll,memory-management,crt,C++,Winapi,Dll,Memory Management,Crt,考虑这种情况: dll = LoadDLL() dll->do() ... void do() { char *a = malloc(1024); } ... UnloadDLL(dll); 此时,调用malloc()时分配的1k是否会再次用于主机进程? DLL正在静态链接到CRT。不,您没有泄漏 如果混合使用dll模型(静态、动态),那么如果在dll中分配内存、在其他dll中释放内存(或在exe中释放内存),最终可能会出现内存错误 这意味着静态链接的CRT创建的堆与不同dl

考虑这种情况:

dll = LoadDLL()
dll->do()

...
void do() {
    char *a = malloc(1024);
}
...

UnloadDLL(dll);
此时,调用malloc()时分配的1k是否会再次用于主机进程? DLL正在静态链接到CRT。

不,您没有泄漏

如果混合使用dll模型(静态、动态),那么如果在dll中分配内存、在其他dll中释放内存(或在exe中释放内存),最终可能会出现内存错误

这意味着静态链接的CRT创建的堆与不同dll的CRT不是同一堆


如果您链接了CRT的动态版本,那么当堆在所有动态链接的CRT之间共享时,就会出现泄漏。这意味着您应该始终将应用程序设计为使用动态CRT,或者确保从不跨dll边界管理内存(即,如果您在dll中分配内存,则始终提供一个例程以在同一dll中释放内存)

可以进行测试,查看是否存在内存泄漏。您运行一个简单的测试30次,每次分配1 MB。你应该很快弄明白

有一件事是肯定的。如果您在DLL中分配了内存,您也应该在那里释放内存(在DLL中)

例如,您应该有如下内容(简单但直观的伪代码):

必须这样做,因为DLL的堆与原始进程(加载DLL)的堆不同。

来自MSDN

CRT库的每个副本都有一个 分离的和不同的状态。像这样的 CRT对象,例如文件句柄, 环境变量和区域设置是 仅对CRT副本有效 这些对象的分配位置或 设置当DLL及其用户使用 CRT库的不同副本, 不能传递这些CRT对象 跨越DLL边界并期望 必须在屏幕上正确拾取它们 另一边

还有,因为每一份CRT 库有自己的堆管理器, 在一个CRT库中分配内存 以及在DLL中传递指针 要由其他对象释放的边界 CRT库的副本是一个潜在的 堆损坏的原因


希望这有帮助。

你说不出来。这取决于静态和动态CRT的实现。它甚至可能取决于分配的大小,因为有CRT将大的分配转发给操作系统,但为小的分配实现自己的堆

CRT泄漏的问题当然是它泄漏。不泄漏的CRT的问题是,可执行文件可能合理地期望使用内存,因为malloc’ed内存应该在调用free之前保持可用

  • 操作系统跟踪的进程使用的内存适用于整个进程,而不是特定于DLL

  • 操作系统将内存分块提供给程序,称为堆

  • 堆管理器(malloc/new等)进一步划分块并将其分发给请求代码

  • 只有在分配新堆时,操作系统才会检测到内存的增加

  • 当DLL静态链接到C运行时库(CRT)时,将编译CRT的私有副本,其中包含DLL代码调用的CRT函数,并将其放入DLL的二进制文件中。Malloc也包括在本文件中

  • 每当静态链接DLL中的代码试图分配内存时,就会调用malloc的这个私有副本

  • 因此,这个malloc从操作系统获取一个只对这个malloc副本可见的私有堆,并在这个私有堆中分配代码请求的内存

  • 当DLL卸载时,它卸载其私有堆,当整个堆返回操作系统时,不会注意到此泄漏

  • 但是,如果动态链接DLL,则内存由单个共享版本的malloc分配,全局分配给在共享模式下链接的所有代码

  • 由这个全局malloc分配的内存来自一个堆,该堆也是用于在动态aka共享模式中链接的所有其他代码的堆,因此是公共的。因此,此堆中的任何泄漏都将成为影响整个过程的泄漏


  • 编辑-添加链接场景的描述。

    实际上,标记的答案不正确。就在那里有个漏洞。虽然每个dll实现自己的堆并在关闭时释放它在技术上是可行的,但大多数“运行时”堆(静态或动态)都是围绕Win32 process heap API的包装器


    除非有人特别注意保证情况并非如此,否则dll将泄漏每个加载、执行、卸载周期的分配。

    谢谢!您能否详细说明CRT如何/何时释放其堆存储?这是由编译器/链接器作为某种默认dll退出代码添加的吗?警告:依赖于实现!CRT可以简单地为进程提供对OS堆的访问,在这种情况下,您可以在DLL中分配,并在退出时调用free out.DllMain。我希望它引用计数,直到dll完全卸载,然后移除堆。MSalters是正确的,例如你可以在任何地方分配和释放,但如果你的应用程序崩溃,你会泄漏内存,只有重新启动才能释放内存(例如Win3.1全局堆)
    dll = DllLoad();
    
    ptr = dll->alloc();
    
    dll->free(ptr);
    
    DllUnload(dll);