10

考虑这种情况:

dll = LoadDLL()
dll->do()

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

UnloadDLL(dll);

此时,调用 malloc() 时分配的 1k 是否会再次可供宿主进程使用?DLL 静态链接到 CRT。

4

6 回答 6

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

  2. 内存由操作系统以块的形式提供给程序,称为堆

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

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

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

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

  7. 因此,只有这个 malloc 副本可见的私有堆是由这个 malloc 从操作系统获取的,它分配这个私有堆中的代码请求的内存。

  8. 当 DLL 卸载时,它会卸载其私有堆,并且当整个堆返回给操作系统时,这种泄漏不会被注意到

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

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

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

于 2008-09-25T10:12:57.153 回答
6

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

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

于 2008-09-25T10:00:02.397 回答
4

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

CRT 库的每个副本都有一个单独且不同的状态。因此,文件句柄、环境变量和语言环境等 CRT 对象仅对分配或设置这些对象的 CRT 副本有效。当 DLL 及其用户使用 CRT 库的不同副本时,您不能将这些 CRT 对象传递到 DLL 边界并期望它们在另一端被正确拾取。

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

希望这可以帮助。

于 2008-09-25T09:38:56.957 回答
4

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

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

于 2010-04-12T18:29:54.423 回答
2

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

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

例如你应该有这样的东西(简单但直观的伪代码):

dll = DllLoad();

ptr = dll->alloc();

dll->free(ptr);

DllUnload(dll);

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

于 2008-09-25T09:26:59.370 回答
-1

不,你不会泄漏。

如果您混合 dll 模型(静态、动态),那么如果您在 dll 中分配内存,您最终可能会出现内存错误,您可以在不同的模型中释放(或在 exe 中释放)

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

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

于 2008-09-25T09:26:54.160 回答