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

时间:2020-03-06 14:42:38  来源:igfitidea点击:

考虑这种情况:

dll = LoadDLL()
dll->do()

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

UnloadDLL(dll);

此时,对malloc()的调用中分配的1k是否可再次用于主机进程?
DLL静态链接到CRT。

解决方案

不,我们不会泄漏。

如果混合使用dll模型(静态,动态),则在dll中分配内存时,我们可能会遇到内存错误,而在另一个内存中释放(或者在exe中释放)

这意味着由静态链接的CRT创建的堆与不同的DLL的CRT不同。

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

一个人可以做一个测试,看看是否有内存泄漏。我们运行30次简单测试,每次分配1 MB。我们应该很快弄清楚。

有一件事是肯定的。如果我们在DLL中分配了内存,则还应该释放该内存(在DLL中)。

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

dll = DllLoad();

ptr = dll->alloc();

dll->free(ptr);

DllUnload(dll);

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

从MSDN跨DLL边界传递CRT对象的潜在错误

Each copy of the CRT library has a
  separate and distinct state. As such,
  CRT objects such as file handles,
  environment variables, and locales are
  only valid for the copy of the CRT
  where these objects are allocated or
  set. When a DLL and its users use
  different copies of the CRT library,
  you cannot pass these CRT objects
  across the DLL boundary and expect
  them to be picked up correctly on the
  other side.
  
  Also, because each copy of the CRT
  library has its own heap manager,
  allocating memory in one CRT library
  and passing the pointer across a DLL
  boundary to be freed by a different
  copy of the CRT library is a potential
  cause for heap corruption.

希望这可以帮助。

你不知道这取决于静态和动态CRT的实现。它甚至可能取决于分配的大小,因为有些CRT将大型分配转发给OS,但为小分配实现自己的堆。

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

  • OS跟踪的进程使用的内存适用于整个进程,而不特定于DLL。
  • 内存由操作系统以块的形式分配给程序,称为堆
  • 堆管理器(malloc / new等)进一步划分了块并将其交给请求代码。
  • 仅当分配了新堆时,操作系统才会检测到内存增加。
  • 当DLL静态链接到C运行时库(CRT)时,带有DLL代码调用的CRT函数的CRT私有副本将被编译并放入DLL的二进制文件中。 Malloc也包含在其中。
  • 只要静态链接的DLL中存在的代码尝试分配内存,就会调用malloc的此私有副本。
  • 因此,此malloc从OS获取了仅对该malloc副本可见的私有堆,并且该私有堆在该私有堆中分配了代码请求的内存。
  • 当DLL卸载时,它会卸载其私有堆,随着整个堆返回到OS,这种泄漏就不会引起注意。
  • 但是,如果DLL是动态链接的,则内存是由malloc的单个共享版本分配的,该共享版本对于在共享模式下链接的所有代码都是全局的。
  • 该全局malloc分配的内存来自一个堆,该堆也是用于以动态aka共享模式链接的所有其他代码的堆,因此是常见的。因此,此堆中的任何泄漏都将成为影响整个过程的泄漏。

编辑添加了链接方案的描述。

实际上,标记的答案是错误的。那个权利存在泄漏。尽管从技术上来说,每个DLL都可以实现自己的堆并在关机时释放它,但大多数"运行时"堆的静态或者动态堆都是Win32进程堆API的包装器。

除非特别注意确保不是这种情况,否则dll将在每次加载,执行,卸载周期中泄漏分配。