Почему не существует метода Assembly.Unload?

Оригинал здесь (статья старая - от 31 мая 2004).[Перевод не проверен]

    Нас часто спрашивают: "Почему Вы не можете реализовать выгрузку сборки из домена приложения? В конце концов, с целью выгрузки DLL Вы могли бы в неуправляемом коде реализовать метод FreeLibrary(), а в CLR использовать эту DLL?"1.

Причины необходимости метода Unload

    Есть обычно две причины, по которым Вы можете пожелать выгрузить сборку: освобождение памяти и управление версиями. Прежде всего очевидно, что Вы хотите освободить выделенную в процессе память. Вторая причина обращается к желанию загрузить более новую версию сборки, как это делает к примеру Asp.Net, когда Вы компилируете новую версию своего приложения. Если выбранный файл сборки заблокирован в файловой системе, то Вы не сможете заменить его.

Почему бы не предоставить это?

    Есть несколько проблем с выгрузкой отдельной сборки. Некоторые из них возникают на аппаратному уровне, в то время как другие проявляются в процессе работы:
  1.    Прежде всего - Вы выполняете код в домене приложения (хмм..!). Это означает, что могут существовать обращающиеся к нему сайты и стеки вызовов с адресами в них, которые ожидают продолжение работы этого домена. Вы когда-либо получали нарушение прав доступа, где Ваш EIP указывает на 0x???????? Это - пример того, что кто-то где-то освободил DLL, в следствие чего страницы уже не отображаются системой памяти, а Вы затем попытались обратиться к этой DLL. Это обычно происходит в COM, когда Вы имеете ссылку на счётчик ошибок, и делаете интерфейсный вызов метода. Мы не можем позволить себе таких потерь при работе с управляемым кодом. Мы должны гарантировать, что знаем весь код, который Вы выполняете и что это является безопасным с точки зрения типов и поддаётся проверке. Это означает явное отслеживание чего-либо, что могло использовать такой код, включая объекты GC и взаимодействующие с COM обёртки. Это отслеживание обрабатывается сегодня вокруг границ домена приложения. Отслеживание этого на уровне сборки становится довольно дорогим.
  2.    Предположим, что Вы действительно отслеживаете и управляете всеми дескрипторами и ссылками на уже работающий код сборки. Предположим, что вы не создавали ngen3 код, поэтому, как только Вы успешно освободили сборку - на самом деле Вами были освобождены только метаданные и IL. Код JIT'd все еще занимает память в "куче" загрузчика домена приложения (методы JIT'd размещаются в буфере последовательно, в том порядке, в котором их вызывают). Теперь мы действительно знаем идентификационные данные всех методов, и таким образом могли бы возвратиться, чтобы разыскать весь код и превратить "кучу" в "кучу" стиля malloc4 со свободным списком (фактически, у нас было дрожание подачи кода, мы моделировали обратный путь, когда на Windows CE, который сделал именно это, чтобы ограничить полный размер "кучи" JIT). Таким образом, эта проблема разрешима и попадает только в перечень работ, выполненных с маленьким успехом при размещении и jitted-размещении метода (достаточно вероятно, чтобы быть масштабной)5.
  3.    Заключительная проблема имеет отношение к коду, который был загружен с целью совместного использования, иначе формально более известный как как "нейтральный домен" (параметр check out /shared в утилите ngen). В этом режиме код для блока сгенерирован так, чтобы он мог быть выполнен в любом домене приложения (отсутствуют жёсткие зависимости). В этом случае имеется немного интересных компромиссов: с одной стороны Вы можете выполнить тот же самый код в любом домене приложения -таким образом, этот код будет загружаться быстрее во всех последующих экземплярах домена приложения (поскольку он уже там имеется). С другой стороны код должен отслеживаться во всех доменах приложения, в которые он когда-либо загружался, прежде чем он сможет быть освобожден. С текущими версиями v1.0 и v1.1 продукта даже не будет возможно выгрузить нейтральный код домена из-за этих ограничений.
    В завершении Вы должны получить ощущение того, что эта особенность не является невозможной для реализации, но она так же не является и тривиальной. В сущности, мы уже реализовали проект вокруг понятия домена приложения и амортизировали его по этой модели через сборки. Я не буду исключать возможности наличия такой функции в будущем. Но это не запланировано продукт Whidbey в настоящее время.

Так какова альтернатива?

    Рекомендуем Вам разрабатывать своё приложение в естественных границах домена, в рамках которого выгрузка полностью поддерживается. Например: Asp.Net размещает приложения в домене приложения и выгружает их, когда они больше не являются необходимыми или устарели. SQL-сервер Юкон делает ту же самую вещь для SQL/CLR. Кроме того, Вы можете использовать функцию теневого копитования (Shadow Copy) Fusion, чтобы избежать ошибки использования заблокированного файла (это так же используется в Asp.Net). Дополнительное преимущество этой модели состоит в том, что Вы можете записать ваш хост для контроля и прервать домены приложения, которые уличены в плохом поведении (таком как использование слишком большого количества памяти или ресурсов). Это может быть очень полезно, когда Вы размещаете потенциально недоверительный или потенциально менее устойчивый код. 

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

Примечания переводчика:
1 - не уверен в правильности перевода этой статьи.
2 - сообщение об ошибке.
3 - информацию об ngen.exe можно почитать здесь.
4 - функция динамического выделения памяти в C/C++, определена в заголовочном файле malloc.h.
5 - сам не понял перевода.


Comments