Как DLLs загружаются CLR?

Мое предположение всегда было, что CLR загрузил все DLLs, в которых это нуждалось на запуске домена приложения. Однако я записал пример, который заставляет меня подвергнуть сомнению это предположение. Я запускаю свое приложение и проверку для наблюдения, сколько модулей загружается.

Process[] ObjModulesList;
ProcessModuleCollection ObjModulesOrig;

//Get all modules inside the process
ObjModulesList = Process.GetProcessesByName("MyProcessName");
// Populate the module collection.
ObjModulesOrig = ObjModulesList[0].Modules;

Console.WriteLine(ObjModulesOrig.Count.ToString());

Я затем repeate тот же самый код и мое количество отличаюсь. Дополнительный DLL является C:\WINNT\system32\version.dll.

Я действительно смущен относительно того, почему количества отличались бы.

Кто-то мог уточнить то, что делает CLR и как он загружает их вещь, и тем, какую логику он делает так?

14
задан John Topley 3 June 2010 в 15:22
поделиться

4 ответа

Нижеследующее скопировано из превосходной книги Дона Бокса Essential .Net. (available here)
(и, имхо, это обязательная вещь для любого профессионального разработчика .Net)

CLR Loader

CLR Loader отвечает за загрузку и инициализацию сборок, модулей, ресурсов и типов. Загрузчик CLR загружает и инициализирует настолько мало, насколько это возможно. В отличие от загрузчика Win32, загрузчик CLR не разрешает и не загружает автоматически подчиненные модули (или сборки). Скорее, подчиненные модули загружаются по требованию, только если они действительно нужны (как в функции отложенной загрузки Visual C++ 6.0). Это не только ускоряет время инициализации программы, но и уменьшает количество ресурсов, потребляемых работающей программой. В CLR загрузка обычно запускается компилятором just in time (JIT) на основе типов. Когда JIT-компилятор пытается преобразовать тело метода из CIL в машинный код, ему необходим доступ к определению типа объявляющего типа, а также к определениям типов для полей типа. Более того, JIT-компилятору также необходим доступ к определениям типов, используемых любыми локальными переменными или параметрами JIT-компилируемого метода. Загрузка типа подразумевает загрузку сборки и модуля, содержащих определение типа. Такая политика загрузки типов (а также сборок и модулей) по требованию означает, что части программы, которые не используются, никогда не попадают в память. Это также означает, что в работающем приложении со временем будут загружаться новые сборки и модули, поскольку типы, содержащиеся в этих файлах, необходимы в процессе выполнения. Если вы не хотите такого поведения, у вас есть два варианта. Первый - просто объявить скрытые статические поля типов, которые вы хотите взаимодействовать с загрузчиком в явном виде.

Загрузчик обычно выполняет свою работу неявно от вашего имени. Разработчики могут взаимодействовать с загрузчиком в явном виде через загрузчик сборок. Загрузчик сборок доступен разработчикам через статический метод LoadFrom класса System.Reflection.Assembly. Этот метод принимает строку CODEBASE, которая может быть либо путем к файловой системе, либо унифицированным локатором ресурсов (URL), идентифицирующим модуль, содержащий манифест сборки. Если указанный файл не может быть найден, загрузчик выбросит исключение System.FileNotFoundException. Если указанный файл может быть найден, но не является модулем CLR, содержащим манифест сборки, загрузчик выдаст исключение System.BadImageFormatException. Наконец, если CODEBASE - это URL, использующий схему, отличную от file:, вызывающая сторона должна иметь права доступа WebPermission, иначе возникнет исключение System.SecurityException. Кроме того, сборки по URL с протоколами, отличными от file:, перед загрузкой сначала загружаются в кэш загрузки.

В листинге 2.2 показана простая программа на C#, которая загружает сборку, расположенную по адресу file://C:/usr/bin/xyzzy.dll, а затем создает экземпляр содержащегося типа с именем AcmeCorp.LOB.Customer. В этом примере все, что предоставляется вызывающей стороной, - это физическое расположение сборки. Когда программа использует загрузчик сборок таким образом, CLR игнорирует состоящее из четырех частей имя сборки, включая номер ее версии.

Пример 2. 2. Загрузка сборки с явной кодовой базой

using System;
using System.Reflection;
public class Utilities {
  public static Object LoadCustomerType() {
    Assembly a = Assembly.LoadFrom(
                    "file: //C:/usr/bin/xyzzy. dll") ;
    return a.CreateInstance("AcmeCorp.LOB.Customer") ;
  }
}

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

Разрешитель сборок доступен разработчикам через метод Load класса System.Reflection.Assembly. Как показано в листинге 2.3, этот метод принимает имя сборки из четырех частей (либо в виде строки, либо в виде ссылки на AssemblyName) и внешне похож на метод LoadFrom, используемый загрузчиком сборок. Это сходство лишь поверхностное, поскольку метод Load сначала использует преобразователь сборок для поиска подходящего файла с помощью довольно сложной серии операций. Первая из этих операций - применение политики версий для определения того, какая именно версия нужной сборки должна быть загружена.

Пример 2.3. Загрузка сборки с помощью Assembly Resolver

using System;
using System.Reflection;
public class Utilities {
  public static Object LoadCustomerType() {
    Assembly a = Assembly.Load(
      "xyzzy, Version=1. 2. 3.4, " +
      "Culture=neutral, PublicKeyToken=9a33f27632997fcc") ;
    return a.CreateInstance("AcmeCorp.LOB.Customer") ;
  }
}
24
ответ дан 1 December 2019 в 09:59
поделиться

CLR загружает сборки по запросу. Когда вы выполняете метод, он смотрит, где он находится (какой модуль и т. Д.), И, если он не загружен, загружает его.

Вот статья о производительности CLR, в которой говорится о загрузке сборок:

Когда CLR JIT компилирует метод Start, ей необходимо загрузить все сборки, на которые есть ссылки в этом методе. . Это означает, что все сборки, указанные в обработчике исключений, будут загружены, даже если они могут не понадобиться большую часть времени выполнения приложения.

Эта статья предназначена для SilverLight, но в ней немного говорится о том, что происходит с CLR.

1
ответ дан 1 December 2019 в 09:59
поделиться

COM DLL загружаются по запросу всякий раз, когда создается соответствующий COM-объект. Это также может происходить с библиотеками, отличными от COM.

0
ответ дан 1 December 2019 в 09:59
поделиться

Это немного не относится к вашему вопросу, но вы можете загрузить все сборки сразу, если вы предпочитаете, чтобы это не происходило по требованию. Что-то вроде этого должно сработать

foreach (AssemblyName asn in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
{
    var asm = Assembly.Load(fn);
    // I've found get types does a good job of ensuring the types are loaded.
    asm.GetTypes();
}
2
ответ дан 1 December 2019 в 09:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: