考虑这种情况:
dll = LoadDLL()
dll->do()
...
void do() {
char *a = malloc(1024);
}
...
UnloadDLL(dll);
此时,调用 malloc() 时分配的 1k 是否会再次可供宿主进程使用?DLL 静态链接到 CRT。
考虑这种情况:
dll = LoadDLL()
dll->do()
...
void do() {
char *a = malloc(1024);
}
...
UnloadDLL(dll);
此时,调用 malloc() 时分配的 1k 是否会再次可供宿主进程使用?DLL 静态链接到 CRT。
操作系统跟踪的进程使用的内存适用于整个进程,而不是特定于 DLL。
内存由操作系统以块的形式提供给程序,称为堆
堆管理器(malloc / new 等)进一步划分块并将其分发给请求代码。
只有在分配新堆时,操作系统才会检测到内存增加。
当 DLL 静态链接到 C 运行时库 (CRT) 时,将编译具有 DLL 代码调用的 CRT 函数的 CRT 的私有副本并将其放入 DLL 的二进制文件中。Malloc也包含在其中。
每当静态链接 DLL 中的代码尝试分配内存时,就会调用 malloc 的这个私有副本。
因此,只有这个 malloc 副本可见的私有堆是由这个 malloc 从操作系统获取的,它分配这个私有堆中的代码请求的内存。
当 DLL 卸载时,它会卸载其私有堆,并且当整个堆返回给操作系统时,这种泄漏不会被注意到。
但是,如果 DLL 是动态链接的,则内存由 malloc 的单个共享版本分配,对以共享模式链接的所有代码都是全局的。
由这个全局 malloc 分配的内存来自一个堆,该堆也是用于在动态 aka 共享模式中链接的所有其他代码的堆,因此很常见。因此,来自该堆的任何泄漏都会成为影响整个过程的泄漏。
编辑 - 添加了链接场景的描述。
你说不出来。这取决于您的静态和动态 CRT 的实现。它甚至可能取决于分配的大小,因为有 CRT 将大分配转发给操作系统,但为小分配实现自己的堆。
泄漏的 CRT 的问题当然是泄漏。不泄漏的 CRT 的问题是可执行文件可能合理地期望使用内存,因为 malloc 的内存应该在调用 free 之前保持可用。
来自 MSDN跨 DLL 边界传递 CRT 对象的潜在错误
CRT 库的每个副本都有一个单独且不同的状态。因此,文件句柄、环境变量和语言环境等 CRT 对象仅对分配或设置这些对象的 CRT 副本有效。当 DLL 及其用户使用 CRT 库的不同副本时,您不能将这些 CRT 对象传递到 DLL 边界并期望它们在另一端被正确拾取。
此外,由于 CRT 库的每个副本都有自己的堆管理器,因此在一个 CRT 库中分配内存并将指针跨过 DLL 边界传递以由 CRT 库的不同副本释放是堆损坏的潜在原因。
希望这可以帮助。
实际上,标记的答案是不正确的。那就是有泄漏。虽然每个 dll 实现自己的堆并在关机时释放它在技术上是可行的,但大多数“运行时”堆(静态或动态)都是 Win32 进程堆 API 的包装器。
除非特别注意保证不是这种情况,否则 dll 将在每个加载、执行、卸载周期泄漏分配。
可以做一个测试,看看是否有内存泄漏。您运行一个简单的测试 30 次,每次分配 1 MB。你应该很快弄清楚。
有一件事是肯定的。如果您在 DLL 中分配了内存,您还应该在那里(在 DLL 中)释放该内存。
例如你应该有这样的东西(简单但直观的伪代码):
dll = DllLoad();
ptr = dll->alloc();
dll->free(ptr);
DllUnload(dll);
必须这样做,因为 DLL 的堆与原始进程(加载 dll)不同。
不,你不会泄漏。
如果您混合 dll 模型(静态、动态),那么如果您在 dll 中分配内存,您最终可能会出现内存错误,您可以在不同的模型中释放(或在 exe 中释放)
这意味着静态链接的 CRT 创建的堆与不同 dll 的 CRT 不同。
如果您与 CRT 的动态版本链接,那么您将有泄漏,因为堆在所有动态链接的 CRT 之间共享。这意味着您应该始终将您的应用程序设计为使用动态 CRT,或者确保您永远不会跨 dll 边界管理内存(即,如果您在 dll 中分配内存,请始终提供一个例程以在同一个 dll 中释放它)