在所有appdomains中加载的混合模式程序集

时间:2014-07-07 13:34:36

标签: c# appdomain mixed-mode

我们的应用程序设置了一个AppDomain,我们将一些模块加载到其中。其中一个模块使用传统混合模式DLL与H5文件进行交互。问题是,只要加载混合模式DLL,句柄就会放在新的AppDomain中,也会放在应用程序启动时创建的原始AppDomain中。

当我们卸载该AppDomain时,新AppDomain中的句柄自然消失,但原始AppDomain中的句柄仍然存在并被固定。这导致我们的托管堆碎片化。

我无法在混合模式源代码中找到可解释此行为的任何内容。唯一看起来可疑的是使用本机静态字符串的以下调用:

H5Utils::throwError( String^ message ) {
  String^ stackStr = gcnew String( H5Utils::errorStack_.c_str() );
  String^ myMessage = message + "\n\nError stack: " + stackStr;
  throw gcnew H5IOError( myMessage );
}

其中H5Utils::errorStack_.c_str()是本机静态字符串。但是这个方法永远不会被调用,并且在加载混合模式DLL时模块会立即加载到两个AppDomains中。

有谁知道为什么会在两个AppDomains中创建一个句柄?

1 个答案:

答案 0 :(得分:2)

所以我更多地挖掘了这个并找到了答案,所以如果有人遇到同样的问题,我想我会回答我自己的问题。

将模块加载到两个AppDomain中的原因是由于使用了本机全局值和静态成员值。这实际上是有意义的,因为这些值是在本机堆上分配的,如果在多个AppDomain中使用此模块,则您仍然希望共享这些值的可能性很高。

问题是当您不断创建和销毁AppDomain并加载这些类型的资源时,因为第一个AppDomain添加到正在运行的进程的引用是静态的,因此会导致固定。这使托管堆碎片化并且长时间运行的进程将开始占用内存。

为了解决这个问题,Microsoft添加了__declspec(appdomain),它应该被添加到本机类型的全局变量中,这使它们只驻留在创建模块的AppDomain中。启用/clr:pure也会将此作为声明。其中大部分描述于 msdn's help on appdomain

最后一个问题是你必须知道你的#include文件。例如,#include <string>将导致模块在AppDomains之间共享,而#include <stdio.h>则不会。