Эффект LoaderOptimizationAttribute
Я написал небольшой фрагмент кода, касающийся динамической загрузки сборок и создания экземпляров классов из этих сборок, включая исполняемый файл, тестовую библиотеку для динамической загрузки и библиотеку загрузчика для загрузки динамической сборки в новую 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
мне не так понятно. ЯвляетсяMultiDomainHost
только для сборок GAC? Для моей ситуации, которая более подходит?Согласно этому
JIT-скомпилированный код нельзя использовать совместно для сборок, загруженных в контекст загрузки из, используя метод LoadFrom класса Assembly, или загруженных из изображений с использованием перегрузок метода Load, которые задают байтовые массивы.
Итак, как я могу определить, загружена ли сборка независимо от домена или нет? Как я могу заверить, что он загружен домен-нейтрально?
1 ответ
Этот атрибут действует только в том случае, если вы предварительно компилируете свои сборки с помощью NGen, чтобы ускорить горячий запуск приложения. Когда вы указываете MultiDomain
или же MultiDomainHost
Вы разрешаете использование предварительно скомпилированных (ngenned) сборок. Вы можете проверить это с помощью Process Explorer, где вы можете посмотреть список загруженных модулей.
Это один из самых больших способов экономии времени при запуске, если ваше приложение состоит из нескольких исполняемых экземпляров, которые совместно используют сборки. Это позволяет.NET обмениваться кодовыми страницами между процессами, что, в свою очередь, экономит реальную память (одна сборка существует только один раз в физической памяти, но совместно используется одним или несколькими процессами) и предотвращает повторное использование одного и того же кода JIT в каждом процессе снова и снова что требует времени за счет того, что сгенерированный код немного менее эффективен, чем это может быть, когда он будет скомпилирован с обычным JIT, который может использовать более динамические данные для генерации наиболее эффективного кода.
В вашем примере вы загружаете сборку в байтовый массив, который находится в управляемой куче и увеличивает частное число байтов. Это делает невозможным обмен данными между процессами. Только процессы, доступные только для чтения и имеющие копию на жестком диске, могут быть разделены между процессами. Это причина, почему атрибут не имеет никакого эффекта. Если вам нужен фактор 2 теплых стартапов, то этот атрибут вы искали. Для всего остального это не актуально.
Теперь вернемся к исходному вопросу:
- Это установлено, но когда вы запускаете приложение под отладчиком, это
MultiDomain
атрибут игнорируется. Когда вы запустите его вне отладчика, вы получите ожидаемые результаты. - да
MultiDomainHost
действительно позволяетAppDomain
нейтральность только для подписанных сборок, все остальные не передаются. - Совместное использование кода может произойти только тогда, когда он предварительно скомпилирован. Реальный вопрос: как проверить, предварительно ли скомпилирована сборка? Я делаю это с помощью Process Explorer, просматривая список загруженных модулей. Когда моя загруженная сборка отображается с путем к кэшу Native Image и расширением.ni, я уверен, что предварительно скомпилированное изображение используется. Вы также можете проверить это с помощью fuslogvw, когда вы установите переключатель в "Собственные изображения", чтобы проверить, почему собственные изображения не использовались во время выполнения.