Являются ли встроенные ресурсы в сборке .NET загруженными с диска или из памяти во время выполнения?

Когда я использую GetManifestResourceStream для извлечения встроенного ресурса из сборки .NET, какой тип ввода-вывода задействован?

Я вижу две возможности:

  • Вся сборка уже была вставлена ​​в память, когда .NET ее загрузил, поэтому GetManifestResourceStream просто обращается к памяти.

  • Только часть кода сборки была помещена в память, когда сборка была загружена .NET, поэтому GetManifestResourceStream нужно вернуться к файлу .dll, чтобы извлечь встроенный ресурс.

Я уверен, что это первый случай, особенно потому, что сборки могут динамически загружаться из необработанных данных с помощью Assembly.Load(Byte[]). Но тогда мне интересно, что произойдет, если будет добавлен очень большой файл (скажем, несколько гигабайт) - второй вариант может быть более эффективным. Имеет ли значение размер?

Просто оспаривая некоторые давние предположения и не в состоянии найти много ссылок на это.

Ответ 1

"Память" - это недостаточно точный термин для операционной системы с виртуальной памятью по запросу, такой как Windows, Linux, MacOS. CLR отображает сборку в адресное пространство процесса, используя файл с отображением в памяти (MMF). Просто числа для процессора, по одному на каждые 4096 байт. Пока что из файла ничего не читается.

Это задерживается до тех пор, пока программа не попытается прочитать с адреса внутри адресного пространства. При первом доступе генерируется ошибка страницы, ядро выделяет ОЗУ для страницы и заполняет ее содержимым файла. После чего программа возобновляется, как будто ничего не произошло. Сильно расширяет возможности виртуальной памяти "вы не платите за то, что не используете".

Здесь нет "извлечения", вы читаете данные ресурса непосредственно из памяти, наиболее эффективным способом, которым это могло бы быть реализовано. Внедренный ресурс не ведет себя иначе, чем другие данные в файле, такие как метаданные и MSIL. Вы также не платите ни за какой код в сборке, которую вы никогда не вызываете.

Имейте в виду, что встроенный ресурс занимает тот же ресурс, что и куча GC, он также требует адресного пространства. Единственное реальное отличие состоит в том, что адресное пространство кучи GC поддерживается файлом подкачки ОС и никогда не может использоваться другими процессами, данные сборки поддерживаются файлом сборки и могут использоваться совместно. Большие ресурсы значительно сокращают объем памяти, который вы можете выделить в программе .NET, даже если вы никогда не используете их. Это имеет значение только в 32-битном процессе, 64-битный процесс имеет много терабайт адресного пространства.

Другое ограничение заключается в том, что представление MMF никогда не может превышать 2 ГБ, даже в 64-разрядном процессе, который устанавливает жесткий верхний предел максимального размера ресурса. Обычно это происходит очень рано, что приводит к сбою сборки с CS1566: "Указанный аргумент вышел за пределы допустимых значений". Не очень хорошая диагностика, кстати.