释放在不同 DLL 中分配的内存

Posted

技术标签:

【中文标题】释放在不同 DLL 中分配的内存【英文标题】:Freeing memory allocated in a different DLL 【发布时间】:2009-10-28 02:01:40 【问题描述】:

我有一个使用另一个 DLL 文件的 DLL 文件的 EXE 文件。出现了这种情况:

在 DLL 文件 1 中:

class abc

    static bool FindSubFolders(const std::string & sFolderToCheck, 
                               std::vector< std::string > & vecSubFoldersFound);

在 DLL 文件 2 中:

void aFunction()

    std::vector<std::string> folders;
    std::string sLocation;
    ...
    abc::FindSubFolders(sLocation, folders)

在发布模式下,一切正常。但在调试模式下,我在文件夹向量中std::strings 之一的析构函数中遇到断言失败(当文件夹在 aFunction 结束时超出范围时):

dbgheap.c : line 1274

/*
 * If this ASSERT fails, a bad pointer has been passed in. It may be
 * totally bogus, or it may have been allocated from another heap.
 * The pointer MUST come from the 'local' heap.
 */
_ASSERTE(_CrtIsValidHeapPointer(pUserData));

我认为这是因为内存已在 DLL 文件 1 的堆上分配,但在 DLL 文件 2 中被释放。

dbgheap.c 中的评论似乎非常坚持认为这是一个问题。

如果我只是忽略它似乎可以正常工作,为什么会出现这样的问题?有没有一种非断言失败的方式来做到这一点?

【问题讨论】:

不是。忽略。它。那是。为什么。我问。 =P 只是想知道为什么这是个问题。 【参考方案1】:

正如肖恩已经说过的,发布版本只是忽略了删除语句,所以你能希望的最好的就是内存泄漏。

如果您可以控制这两个 DLL 文件的编译方式,请确保对运行时库使用多线程调试 DLL (/MDd) 或多线程 DLL (/MD) 设置。这样,两个 DLL 文件将使用相同的运行时系统并共享相同的堆。

缺点是您需要将运行时系统与您的应用程序一起安装(微软为此提供了安装程序)。它可以在您的开发机器上正常工作,因为 Visual Studio 也安装了该运行时系统,但在新安装的机器上它会报告缺少 DLL 文件。

【讨论】:

我的项目属性错误 - 我没有设置调试 DLL (/MDd) - 现在可以正常工作了。谢谢!【参考方案2】:

很可能,发布版本有同样的问题,但发布版本没有断言。他们只是忽略了这个问题。你可能永远看不到问题。或者您可能会看到数据损坏。或者您可能会看到崩溃。也许只有您的用户会遇到您根本无法重现的错误。

不要忽略 CRT 断言。

您应该始终使用适当的解除分配器(与开始使用的分配器匹配的解除分配器)。如果您在 DLL 文件中使用静态 CRT 库,则 DLL 文件使用不同的堆。您不能跨堆释放内存。使用同一个堆分配和释放一块内存。

如果您在 DLL 文件中使用共享 CRT 库,那么它们应该使用相同的堆,您可以在一个 DLL 文件中分配并在另一个 DLL 文件中释放。

【讨论】:

参见。陈瑞文:blogs.msdn.com/oldnewthing/archive/2006/09/15/755966.aspx【参考方案3】:

正如其他人所说,可以通过确保 CRT 在两个模块之间共享来解决问题。但在某些常见情况下,该合同难以执行。

原因是,如果 EXE 和 DLL 没有链接到同一个 CRT 版本(如 6.0、7.0、8.0),那么确保链接到共享 CRT 将不起作用。例如,如果您使用 VC6.0 中构建的 DLL 并尝试将其与 VS2010 中的 EXE 构建一起使用,您将遇到与以前相同的问题。这两个版本的 CRT 将加载到您的进程中,并且每个版本都使用自己的堆进行分配,无论您的 EXE 和 DLL 是否使用“共享”CRT,它们都不会相同。

API 的更好做法是确保在一侧分配的对象也在同一侧销毁。我知道这听起来很难看,但这是确保 DLL 保持二进制兼容的唯一方法。

【讨论】:

会使用多态分配器版本的vectorstring 工作吗?因为他们会调用多态分配函数,这将(可能)指向 DLL 内的代码?【参考方案4】:

只有当应用程序或一个(或多个)DLL 文件链接到标准库的静态版本时,这才是问题。大约十年前,MS 通过发布标准库的共享库版本解决了这个问题。这是因为标准库的每个版本都将构建它自己的内部堆,因此对于多个堆,您必须将内存释放到正确的堆。通过使用标准库的共享版本,它们都使用相同的堆。

现在是应用程序的标准做法,所有 DLL 文件都应构建为使用标准库的动态版本。

唯一需要注意的是,如果您创建自己的堆并从该堆分配内存。但这是一个非常专业的程序,仅在极少数情况下进行(如果您足够了解使用它,那么您将不会处于提出这个问题的情况)。

【讨论】:

以上是关于释放在不同 DLL 中分配的内存的主要内容,如果未能解决你的问题,请参考以下文章

C# Interop - 释放在非托管代码中分配的内存

如何释放在我的 RPC 服务器中分配的内存?

释放在另一个循环中调用的函数中的alloated内存

C:如何释放链表中的节点?

由 malloc 在 C 中分配的大小的意外输出 [关闭]

查找程序中分配的内存? [复制]