卸载DLL时出现内存泄漏会导致主进程发生泄漏吗?

10

考虑以下情况:

dll = LoadDLL()
dll->do()

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

UnloadDLL(dll);

此时,通过调用malloc()分配的1k内存将再次可供主进程使用吗? 动态链接库(DLL)静态链接到CRT。

6个回答

9
  1. 操作系统跟踪的进程占用的内存适用于整个进程,而不是特定的动态链接库(DLL)。

  2. 操作系统通过块状分配内存,称为堆,提供给程序。

  3. 堆管理器(如malloc / new等)将块进一步划分并分配给请求代码。

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

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

  6. 仅当静态链接DLL中的代码尝试分配内存时,才会调用此malloc的私有副本。

  7. 因此,此malloc从操作系统获取一个仅对此malloc副本可见的私有堆,并在此私有堆内分配由代码请求的内存。

  8. 当动态链接库卸载时,它将卸载其私有堆,由于整个堆都被返回到操作系统,因此这种泄漏将不会被注意到

  9. 但是,如果DLL是动态链接的,则内存由单个共享版本的malloc分配,该版本对动态链接的所有代码全局可用。

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


6

很难说。这取决于您静态和动态CRT的实现方式。甚至可能取决于分配的大小,因为有些CRT将大型分配转发到操作系统,但对小型分配实现了自己的堆。

CRT泄漏的问题当然是泄漏的问题。而CRT不泄漏的问题在于可执行文件可能合理地期望使用内存,因为malloc分配的内存应该保持可用,直到调用free。


4

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

除非有人特别注意确保这不是情况,否则dll将在每次加载、执行和卸载循环中泄漏分配。


4

来自MSDN 在DLL边界传递CRT对象可能引起潜在错误

CRT库的每个副本都有独立的状态。因此,诸如文件句柄、环境变量和区域设置等CRT对象仅对分配或设置这些对象的CRT副本有效。当DLL及其用户使用不同的CRT库副本时,您不能将这些CRT对象跨越DLL边界传递并期望它们在另一侧被正确捕获。

此外,由于CRT库的每个副本都有自己的堆管理器,因此在一个CRT库中分配内存,并将指针跨越DLL边界传递以由不同的CRT库副本释放,可能导致堆损坏。

希望这可以帮助您。


2

您可以进行测试,查看是否存在内存泄漏。每次分配1 MB的简单测试运行30次即可快速找出问题。

有一件事是确定的。如果您在DLL中分配了内存,则也应该在其中释放该内存。

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

dll = DllLoad();

ptr = dll->alloc();

dll->free(ptr);

DllUnload(dll);

这是必须的,因为DLL具有与加载dll的原始进程不同的堆。


-1

不,你没有泄漏。

如果您混合使用dll模型(静态、动态),则可能会出现内存错误,如果您在一个dll中分配内存,在另一个dll(或exe中)释放它,则可能会出现内存错误。

这意味着静态链接的CRT创建的堆与其他dll的CRT不同。

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


谢谢! 您能详细说明CRT何时释放其堆存储吗?这是由编译器/链接器作为某种默认的dll退出代码添加的吗? - Viktor
4
警告:实现有关!CRT 可能仅为进程提供对操作系统堆的访问权限,在这种情况下,您可以在 DLL 中分配并在外部释放。 - MSalters
2
DllMain将在退出时被调用。我期望它引用计数直到dll完全卸载,并随后删除堆。MSalters是正确的,例如您可以在任何地方分配和释放,但如果您的应用程序崩溃,则会泄漏内存,除非重新启动才能释放(例如Win3.1全局堆)。 - gbjbaanb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接