我们的应用程序设置了一个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中创建一个句柄?
答案 0 :(得分:2)
所以我更多地挖掘了这个并找到了答案,所以如果有人遇到同样的问题,我想我会回答我自己的问题。
将模块加载到两个AppDomain中的原因是由于使用了本机全局值和静态成员值。这实际上是有意义的,因为这些值是在本机堆上分配的,如果在多个AppDomain中使用此模块,则您仍然希望共享这些值的可能性很高。
问题是当您不断创建和销毁AppDomain并加载这些类型的资源时,因为第一个AppDomain添加到正在运行的进程的引用是静态的,因此会导致固定。这使托管堆碎片化并且长时间运行的进程将开始占用内存。
为了解决这个问题,Microsoft添加了__declspec(appdomain)
,它应该被添加到本机类型的全局变量中,这使它们只驻留在创建模块的AppDomain中。启用/clr:pure
也会将此作为声明。其中大部分描述于
msdn's help on appdomain
最后一个问题是你必须知道你的#include
文件。例如,#include <string>
将导致模块在AppDomains之间共享,而#include <stdio.h>
则不会。