6

所以我有一个用 C++ 编写的 DLL。但是,它使用 GlobalAlloc() 分配内存。为了避免内存泄漏,我想跟踪这些分配并在销毁 DLL 时取消分配所有这些分配。

有什么方法可以编写一个在我的 DLL 被卸载时调用的函数?我能想到的一件事是在我的 DLL 中创建一个全局对象并在其析构函数中编写内存释放调用,但这似乎有点过头了。我的另一个想法是仅依靠操作系统在 DLL 卸载时释放内存,但这似乎很脏。

谢谢

4

3 回答 3

7

有什么方法可以编写一个在我的 DLL 被卸载时调用的函数?我能想到的一件事是在我的 DLL 中创建一个全局对象并在其析构函数中编写内存释放调用

这是可能的,尽管我相信你的对象的析构函数何时被调用是不确定的。

您可能对 感兴趣DLL_PROCESS_DETACH,尽管您应该避免在 中做任何重要的事情DllMain,但在这里释放资源似乎是可以接受的。注意注意事项:

当由于 DLL 加载不成功、进程终止或对 FreeLibrary 的调用而从进程中卸载 DLL 时,系统不会使用 DLL_THREAD_DETACH 的各个线程的值调用 DLL 的入口点函数。过程。DLL 仅发送一个 DLL_PROCESS_DETACH 通知。DLL 可以借此机会清理 DLL 已知的所有线程的所有资源。

处理 DLL_PROCESS_DETACH 时,仅当动态卸载 DLL(lpReserved 参数为 NULL)时,DLL 才应释放堆内存等资源。如果进程正在终止(lpvReserved 参数为非 NULL),则除当前线程外,进程中的所有线程要么已经退出,要么已通过调用 ExitProcess 函数显式终止,这可能会留下一些进程资源,例如堆处于不一致的状态。在这种情况下,DLL 清理资源是不安全的。相反,DLL 应该允许操作系统回收内存。

您可能需要详细说明为什么您的 DLL 可以保留内存,如果您有许多由 DLL 创建的对象,它们应该具有定义的生命周期并在其生命结束时自行清理。

如果它们不是对象(即内存被分配并通过函数返回给调用者),为什么不把责任归还给正在使用你的 DLL 的人呢?他们可以释放内存。终端服务库遵循此模式 ( WTSFreeMemory)。

如果资源是长期存在的,并且必须在库的生命周期内存在,则让使用者控制库的生命周期。写两个函数:MyFrameworkStartupMyFrameworkShutdown酌情。Winsock 遵循这种模式(WSAStartupWSACleanup)。

我的另一个想法是仅依靠操作系统在 DLL 卸载时释放内存,但这似乎很脏。

如果进程正在退出,你会没事的

不用担心释放内存;当进程地址空间被破坏时,它都会消失。不用担心关闭手柄;当进程句柄表被销毁时,句柄会自动关闭。不要尝试调用其他 DLL,因为那些其他 DLL 可能已经收到了它们的 DLL_PROCESS_DETACH 通知,在这种情况下,它们的行为可能与 Delphi 对象的行为异常一样,如果您在其析构函数运行后尝试使用它.

在实施“什么都不做”策略之前,请确保您阅读了整篇文章和评论并理解了它。

于 2012-04-15T00:15:33.677 回答
3

如何/何时分配内存?通常,最明智的选择是尝试保持某种对称性(构造函数分配,析构函数释放。或者在加载 DLL 时分配内存,在卸载 DLL 时释放内存)。

在任何情况下,如果您想在卸载 DLL 时收到通知,请查看DllMain函数,特别是 DLL_PROCESS_DETACH 参数。

于 2012-04-15T00:09:26.267 回答
1

在卸载 DLL 时调用 DllMain 函数,并将 fdwReason 设置为 DLL_PROCESS_DETACH。如文档中所述,请确保检查 lpvReserved 的值,并且只有在它为 NULL 时才释放内存;如果进程正在终止,则不应释放内存

于 2012-04-15T00:11:53.380 回答