.NET 程序集中的嵌入式资源是在运行时从磁盘还是从内存加载的?

Are embedded resources in a .NET Assembly loaded from disk or from memory at runtime?

当我使用 GetManifestResourceStream 从 .NET 程序集中检索嵌入式资源时,涉及哪种 I/O?

我看到两种可能性:

  1. 整个程序集在 .NET 加载时已经放入内存,所以 GetManifestResourceStream 只是访问内存。

  2. 程序集在.NET加载时只将程序集的代码部分放入内存,所以GetManifestResourceStream需要回到.dll文件中提取嵌入式资源。

我很确定是第一种情况,特别是因为可以使用 Assembly.Load(Byte[]) 从原始数据动态加载程序集。但是后来我想知道如果嵌入了一个非常大的文件(比如几千兆字节)会发生什么——第二个选项可能更有效。大小重要吗?

只是挑战了一些长期以来的假设,并没有找到太多的参考方法。

"Memory" 在需求分页虚拟内存操作系统(如 Windows、Linux、MacOS 上不是一个足够精确的术语。 CLR 使用内存映射文件 (MMF) 将程序集映射到进程的地址 space。只是处理器的编号,每 4096 字节一个。尚未从文件中读取任何内容。

这会延迟到程序尝试从地址 space 内的地址读取。第一次访问会产生页面错误,内核为页面分配 RAM 并用文件内容填充它。之后程序继续运行,就好像什么也没发生一样。强力强化虚拟内存的"you don't pay for what you don't use"优势。

没有 "extraction",您正在直接从内存中读取资源数据,这是实现它的最有效方式。嵌入资源在其他方面与文件中的其他数据(如元数据和 MSIL)没有任何不同。同样,您无需为从未调用过的程序集中的任何代码付费。

请记住,嵌入式资源占用与 GC 堆相同的 OS 资源,它也需要地址 space。唯一真正的区别是 GC 堆地址 space 由 OS 分页文件支持并且永远不能与其他进程共享,汇编数据由汇编文件支持并且可以共享。大资源会显着减少您可以在 .NET 程序中分配的内存量,即使您从不使用它们也是如此。这仅在 32 位进程中很重要,64 位进程有许多 TB 的地址 space.

另一个限制是 MMF 视图永远不能大于 2 GB,即使在 64 位进程中,这对资源的最大大小设置了硬性上限。这通常很早就结束了,使用 CS1566 构建失败,"Specified argument was out of the range of valid values"。顺便说一句,这不是很好的诊断。