C运行时静态链接和动态链接中的内存分配

3
我的程序架构涉及插件(dll)和exe(显然)。我们是唯一的插件提供者。有人告诉我,在插件中分配内存,然后在exe代码中释放它,如果我们静态链接c运行时,这可能是一个潜在问题。但是,如果我们动态链接它,就只有一个堆栈,c运行时可以访问其中所有内容。基于此建议,我们切换到了动态链接,但是我们从切换中看到的只是与新运行时的分发和安装相关的头疼和麻烦。(不知道我们在内存分配问题方面避免了什么。)
内存分配的影响是什么?假设插件分配内存,exe释放它。静态链接c运行时和动态链接c运行时之间是否有不同的行为?如果我们使用插件,静态链接c运行时会有问题吗?如果我们切换回静态链接,那么会破坏内存泄漏检测和崩溃转储报告吗?
符合评论中提出的一些问题 我应该选择静态还是动态的VC++运行时版本?,特别是我应该选择静态还是动态的VC++运行时版本? 还有一些讨论在http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.100).aspx
3个回答

1
如果您想要在一个模块中分配堆内存,在另一个模块中释放,则只需动态链接运行时即可。而且,所有参与方都必须动态链接相同的运行时。一旦您这样做了,就不会有问题。
现在,这是对插件施加的相当严格的限制,您应该三思而后行。强迫所有插件作者与您同时升级是一项沉重的限制。我建议您考虑重构接口,使得在单个模块内总是将分配与释放匹配。这解除了我上面描述的限制,并使得插件作者的生活更容易。
至于为什么您仍然遭受运行时错误,很难说。我猜测并非所有模块都链接到相同版本的运行时。
在评论中,您表示您控制所有插件。这意味着我上面讨论的限制并不是强制性的,因为对于您来说很容易为所有模块使用相同的编译器版本。然而,跨模块堆访问的动态链接规则仍然存在。您必须针对相同的单个运行时版本使用动态链接。

我需要澄清的细节是:我没有遇到运行时错误。我们是所有插件的作者。发布要求是所有插件必须同时重新编译。 - MrPhilTX
这会让你的生活更轻松。但是,你仍然需要所有参与方使用相同的堆。这意味着针对单个运行时版本进行动态链接。 - David Heffernan
您还可以提供您的插件“allocate”和“release”调用(由可执行文件提供),以便它们可以访问可执行文件的分配器。我倾向于在模块中使用一个“ModuleInit”函数,为其提供指向回调函数的指针表。 - David Schwartz
@david 是的,那是一个选项。我个人更喜欢非常基本,并强制所有分配与同一模块中的释放成对出现。我还会动态链接到运行时以保持图像大小低。 - David Heffernan
我本来以为http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.100).aspx会提到内存分配问题-因为这似乎是跨dll/exe边界最常见的访问,而不是我会在一个中开始strtok并在另一个中完成。我没有找到任何关于此限制的“官方”文档,这让我感到惊讶。所有堆都不在同一地址空间吗? - MrPhilTX
所有堆实际上都在同一地址空间中。只是在静态链接时,当您从模块A调用malloc时,您会得到模块A的堆,然后当您从模块B调用free时,您会得到模块B的堆。 malloc堆和运行时之间存在一对一的关系。 - David Heffernan

0

显然,问题在于插件和主应用程序使用不同且不兼容的堆管理器,这些管理器会破坏彼此的数据结构。使用动态CRT理论上强制两者使用相同的CRT,因此两者自动兼容。

一般情况下,最好让插件负责分配和删除其使用的所有内存。这意味着可能需要向插件添加API,以删除它先前创建并传回主应用程序的对象。这也为插件和应用程序之间提供了良好的隔离,允许插件使用专门的分配器以提高性能或其他原因。COM就是这样做的。


0

分配/释放内存的函数是成对出现的;你不能调用my_malloc()然后使用free(),或者调用C++的“new”运算符,然后从某个随机库中调用FreeMem() :)

您的插件需要一种众所周知的分配和释放内存的方式。如果它使用malloc(),那么您的主程序可能只需使用free()即可。但是,如果它执行更多的操作(Windows有大量可用的内存分配器),则您的插件API需要提供一种让主exe调用插件以释放其数据的方法。

因此,如果您的主程序调用

foo = plugin->allocate_something()

如果您的插件API有相应的

,那么这将是明智的选择。
plugin->free_something (foo)

这样主程序就可以明确无误地使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接