我已经编写了一小段关于动态加载程序集和从这些程序集创建类实例的代码,包括一个可执行文件、一个要动态加载的测试库和一个要将动态程序集加载到新的Appdomain
中的加载程序库。加载程序库同时被可执行程序库和动态库引用。
//executable
[System.STAThreadAttribute()]
[System.LoaderOptimization(LoaderOptimization.MultiDomain)]
static void Main(string[] args)
{
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomain
};
AppDomain childDomain = AppDomain.CreateDomain("MyDomain", null, domainSetup);
Console.WriteLine(AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString());
Console.WriteLine(childDomain.SetupInformation.LoaderOptimization.ToString());
byte[] assembly = null;
string assemblyName = "CSTestLib";
using (FileStream fs = new FileStream(assemblyName+".dll",FileMode.Open))
{
byte[] byt = new byte[fs.Length];
fs.Read(byt,0,(int)fs.Length);
assembly = byt;
}
object[] pararmeters = {assemblyName,assembly};
string LoaderAssemblyName = typeof(AssemblyLoader).Assembly.FullName;
string LoaderClassName = typeof(AssemblyLoader).FullName;
AssemblyLoader assloader = (AssemblyLoader)childDomain.CreateInstanceAndUnwrap(LoaderAssemblyName,LoaderClassName , true, BindingFlags.CreateInstance, null, parameters, null, null);
object obj = assloader.Load("CSTestLib.Class1");
object obj2 = assloader.Load("CSTestLib.Class2");
AppDomain.Unload(childDomain);
Console.ReadKey();
}
//Dynamic Lib
using System;
namespace CSTestLib
{
public class Class1 :MarshalByRefObject
{
public Class1() { }
}
public class Class2 : MarshalByRefObject
{
public Class2() { }
}
}
//Loader Library
using System;
namespace LoaderLibrary
{
public class AssemblyLoader : MarshalByRefObject
{
string assemblyName;
public AssemblyLoader(string assName, byte[] ass)
{
assemblyName = assName;
AppDomain.CurrentDomain.Load(ass);
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + " " + AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString());
}
public object Load(string className)
{
object ret = null;
try
{
ret = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assemblyName, className);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
return ret;
}
}
}
我把
LoaderOptimizationAttribute
设置为main()
方法,但是AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString();
说是NotSpecified
为什么?我不太清楚
MultiDomain
和MultiDomainHost
之间的区别。是否仅适用于GAC程序集?对于我的情况哪一个更合适?根据this
无法为共享JIT编译的代码
从加载到加载中的程序集
上下文,使用的loadFrom方法
程序集类,或从
使用加载重载的图像
指定字节数组的方法。
那么,如何检测程序集是否加载了域中性或非中性?怎样才能保证我的加载域中立?
最佳答案
只有在使用NGen预编译程序集以加快应用程序的热启动时,此属性才有效。当指定MultiDomain
或MultiDomainHost
时,将启用预编译(ngened)程序集的使用。您可以使用进程资源管理器来验证这一点,您可以在其中查看加载模块的列表。
如果应用程序由几个共享程序集的可执行实例组成,则这是最大的启动时间节省程序之一。这使.NET能够在进程之间共享代码页,从而节省实际内存(一个程序集在物理内存中只存在一次,但它在一个或多个进程之间共享),并防止在每个进程中一次又一次地对同一代码进行jit,这将花费时间,代价是生成的代码效率较低,因为它可以用常规JIT编译,它可以使用更多动态数据来生成最高效的代码。
在您的示例中,您将程序集加载到位于托管堆中的字节数组中,并增加您的私有字节计数。这使得无法在进程之间共享数据。只有在硬盘上有对应的页面才能在进程之间共享。这就是属性没有效果的原因。如果你在追求热启动性能的因素2,这就是你所追求的属性。其他的一切都与此无关。
现在回到你最初的问题:
它已设置,但在调试器下启动应用程序时,将忽略此MultiDomain
属性。当您在调试器之外启动它时,您将得到预期的结果。
是的,MultiDomainHost
只对已签名的程序集启用AppDomain
中立性所有其他程序集都不共享。
代码共享只能在预编译时发生。真正的问题是:如何检查程序集是否已预编译?我通过查看已加载模块的列表来使用process explorer。当我加载的程序集显示了指向本机映像缓存的路径和.ni扩展名时,我确信使用了预编译的映像。当您将单选按钮设置为原生图像时,您可以使用FoSLogVW检查这一点,以检查为什么运行时未使用本机映像。
关于c# - LoaderOptimizationAttribute的作用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5783228/