Эффект атрибута LoaderOptimizationAttribute

#c# #appdomain #.net-assembly #dynamic-loading #appdomainsetup

#c# #appdomain #.net-сборка #динамическая загрузка #appdomainsetup

Вопрос:

Я написал небольшой фрагмент кода, касающийся динамической загрузки сборок и создания экземпляров класса из этих сборок, включая исполняемый файл, тестовую библиотеку для динамической загрузки и библиотеку загрузчика для загрузки динамической сборки в новую Appdomain . На библиотеку Loader ссылаются как исполняемый файл, так и динамическая библиотека.

 //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;
        }
    }
}
  
  1. Здесь я установил метод LoaderOptimizationAttribute on main() , но AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString(); говорит, что это NotSpecified почему?

  2. Различия между MultiDomain и MultiDomainHost мне не так понятны. MultiDomainHost Только для сборок GAC? Что больше подходит для моей ситуации?

  3. В соответствии с этим

    JIT-скомпилированный код не может быть совместно использован для сборок, загруженных в контекст load-from с использованием метода LoadFrom класса Assembly, или загруженных из изображений с использованием перегрузок метода Load, которые определяют массивы байтов.

Итак, как я могу определить, загружена сборка независимо от домена или нет? Как я могу гарантировать, что он загружен независимо от домена?

Ответ №1:

Этот атрибут действует только в том случае, если вы предварительно компилируете свои сборки с помощью NGen, чтобы ускорить предварительный запуск вашего приложения. Когда вы указываете MultiDomain или MultiDomainHost разрешаете использование предварительно скомпилированных (ngenned) сборок. Вы можете убедиться в этом с помощью Process Explorer, где вы можете посмотреть на список загруженных модулей.

Это позволяет значительно сэкономить время при запуске, если ваше приложение состоит из нескольких исполняемых экземпляров, совместно использующих сборки. Это позволяет .NET совместно использовать кодовые страницы между процессами, что, в свою очередь, экономит реальную память (одна сборка существует только один раз в физической памяти, но она используется совместно одним или несколькими процессами) и предотвращает повторное использование одного и того же кода снова и снова в каждом процессе, что требует времени за счет того, что сгенерированный код немного менее эффективен, чем это могло бы быть, если бы он был скомпилирован с помощью обычного JIT, который может использовать больше динамических данных для генерации наиболее эффективного кода.

В вашем примере вы загружаете сборку в массив байтов, который расположен в управляемой куче и увеличивает количество ваших личных байтов. Это делает невозможным совместное использование данных между процессами. Только страницы, доступные только для чтения, которые имеют аналог на вашем жестком диске, могут совместно использоваться процессами. Это причина, по которой атрибут не имеет никакого эффекта. Если вам требуется увеличить производительность при быстром запуске в 2 раза, это именно тот атрибут, который вы искали. Для всего остального это не имеет значения.

Теперь вернемся к вашему первоначальному вопросу:

  1. Он установлен, но при запуске приложения в отладчике этот MultiDomain атрибут игнорируется. Когда вы запустите его вне отладчика, вы получите ожидаемые результаты.
  2. Да, MultiDomainHost действительно включает AppDomain нейтральность только для подписанных сборок, все остальные не являются общими.
  3. Совместное использование кода может произойти только тогда, когда он предварительно скомпилирован. Реальный вопрос в том, как проверить, предварительно ли скомпилирована сборка? Я делаю это с помощью Process Explorer, просматривая список загруженных модулей. Когда в моей загруженной сборке отображается путь к собственному кэшу изображений и расширение .ni, я уверен, что используется предварительно скомпилированное изображение. Вы можете проверить это также с помощью fuslogvw, когда вы устанавливаете переключатель в положение «Собственные изображения», чтобы проверить, почему собственные изображения не использовались средой выполнения.

Комментарии:

1. Мне кажется, что атрибут действительно имеет эффект. Запуск с подключенным отладчиком возвращает NotSpecified … запуск без отладчика возвращает MultiDomain … Может быть, кто-нибудь сможет это подтвердить.

2. » MultiDomainHost включает AppDomain нейтральность только для подписанных сборок, все остальные не являются общими». Обратите внимание, что начиная с .NET Framework 2.0, только сборки в GAC являются общими. Недостаточно иметь строгое имя.

3. «имеет эффект только в том случае, если вы предварительно компилируете свои сборки с помощью NGen», «Совместное использование кода может произойти только тогда, когда он предварительно скомпилирован». Это неверно. Совместное использование кода не требует предварительно скомпилированных образов NGEN. Например, с MultiDomainHost любая сборка в GAC может быть разделена между доменами приложений. Это значение по умолчанию для ASP.NET . Несколько приложений, запущенных в одном пуле приложений IIS, будут совместно использовать сборки из GAC и сократят использование памяти (сборка загружается только один раз) и процессора (код компилируется JIT только один раз). По умолчанию для других типов приложений используется значение SingleDomain (без общего доступа).

4. @Лукас: Спасибо за информацию. Я не был осведомлен об этом. Есть ли какая-либо документация в Интернете?