CLR 如何加载 DLL?

发布于 2024-11-16 15:20:01 字数 558 浏览 1 评论 0原文

我的假设始终是 CLR 在应用程序域启动时加载了它所需的所有 DLL。然而,我写了一个例子,让我对这个假设提出质疑。我启动我的应用程序并检查加载了多少模块。

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());

然后我重复完全相同的代码,但我的计数不同。附加的 DLL 是 C:\WINNT\system32\version.dll。

我真的很困惑为什么计数会不同。

有人可以详细说明 CLR 正在做什么以及它如何加载这些东西,以及它是通过什么逻辑这样做的吗?

My assumption was always that the CLR loaded all of the DLLs it needed on startup of the app domain. However, I've written an example that makes me question this assumption. I start up my application and check to see how many modules are loaded.

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());

I then repeate the exact same code and my count is different. The additional DLL is C:\WINNT\system32\version.dll.

I'm really confused as to why the counts would be different.

Could someone please elaborate on what the CLR is doing and how it's loading these thing, and by what logic it's doing so?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

剑心龙吟 2024-11-23 15:20:01

以下内容复制自 Don Box 的优秀Essential .Net。 (可在此处获取)
(恕我直言,这是任何专业 .Net 开发人员必备的)

CLR 加载器

CLR 加载器负责加载和初始化程序集、模块、资源和类型。 CLR 加载程序会尽可能少地加载和初始化。与 Win32 加载程序不同,CLR 加载程序不会解析并自动加载从属模块(或程序集)。
相反,只有在实际需要时才按需加载从属部分(与 Visual C++ 6.0 的延迟加载功能一样)。这不仅加快了程序初始化时间,而且减少了正在运行的程序消耗的资源量。
在 CLR 中,加载通常由基于类型的即时 (JIT) 编译器触发。当 JIT 编译器尝试将方法体从 CIL 转换为机器代码时,它需要访问声明类型的类型定义以及类型字段的类型定义。此外,JIT 编译器还需要访问正在 JIT 编译的方法的任何局部变量或参数所使用的类型定义。加载类型意味着加载包含类型定义的程序集和模块。
这种按需加载类型(以及程序集和模块)的策略意味着程序中未使用的部分永远不会进入内存。这还意味着正在运行的应用程序经常会看到随着时间的推移加载新的程序集和模块,因为在执行期间需要这些文件中包含的类型。如果这不是您想要的行为,您有两种选择。一种是简单地声明您想要与加载器显式交互的类型的隐藏静态字段。

加载程序通常会代表您隐式地完成工作。开发人员可以通过程序集加载器显式地与加载器交互。程序集加载器通过 System.Reflection.Assembly 类上的 LoadFrom 静态方法向开发人员公开。此方法接受 CODEBASE 字符串,该字符串可以是文件系统路径,也可以是标识包含程序集清单的模块的统一资源定位符 (URL)。如果找不到指定的文件,加载器将抛出 System.FileNotFoundException 异常。如果可以找到指定的文件,但不是包含程序集清单的 CLR 模块,则加载程序将引发 System.BadImageFormatException 异常。最后,如果 CODEBASE 是使用 file: 以外的方案的 URL,则调用者必须具有 WebPermission 访问权限,否则将引发 System.SecurityException 异常。此外,URL 上使用除 file: 之外的协议的程序集在加载之前会首先下载到下载缓存。

清单 2.2 显示了一个简单的 C# 程序,该程序加载位于 file://C:/usr/bin/xyzzy.dll 的程序集,然后创建名为 AcmeCorp.LOB 的包含类型的实例.客户。在此示例中,调用者提供的只是程序集的物理位置。
当程序以这种方式使用程序集加载器时,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所示,这个name-to-location解决该过程考虑了多种因素,包括应用程序所在的目录、版本控制策略和其他配置详细信息(所有这些都将在本章后面讨论)。

程序集解析器通过 System.Reflection.Assembly 类的 Load 方法向开发人员公开。如清单 2.3 所示,此方法接受由四部分组成的程序集名称(作为字符串或作为 AssemblyName 引用),表面上看起来与程序集加载器公开的 LoadFrom 方法类似。这种相似性只是表面的,因为 Load 方法首先使用程序集解析器,通过一系列相当复杂的操作来查找合适的文件。这些操作中的第一个是应用版本策略来准确确定应加载所需程序集的哪个版本。

例 2.3。使用程序集解析器加载程序集

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") ;
  }
}

The following copied from Don Box's excellent Essential .Net. (available here)
(and, imho, a must have for any professional .Net developer)

The CLR Loader

The CLR Loader is responsible for loading and initializing assemblies, modules, resources, and types. The CLR loader loads and initializes as little as it can get away with. Unlike the Win32 loader, the CLR loader does not resolve and automatically load the subordinate modules (or assemblies).
Rather, the subordinate pieces are loaded on demand only if they are actually needed (as with Visual C++ 6.0's delay-load feature). This not only speeds up program initialization time but also reduces the amount of resources consumed by a running program.
In the CLR, loading typically is triggered by the just in time (JIT) compiler based on types. When the JIT compiler tries to convert a method body from CIL to machine code, it needs access to the type definition of the declaring type as well as the type definitions for the type's fields. Moreover, the JIT compiler also needs access to the type definitions used by any local variables or parameters of the method being JIT-compiled. Loading a type implies loading both the assembly and the module that contain the type definition.
This policy of loading types (and assemblies and modules) on demand means that parts of a program that are not used are never brought into memory. It also means that a running application will often see new assemblies and modules loaded over time as the types contained in those files are needed during execution. If this is not the behavior you want, you have two options. One is to simply declare hidden static fields of the types you want to interact with the loader explicitly.

The loader typically does its work implicitly on your behalf. Developers can interact with the loader explicitly via the assembly loader. The assembly loader is exposed to developers via the LoadFrom static method on the System.Reflection.Assembly class. This method accepts a CODEBASE string, which can be either a file system path or a uniform resource locator (URL) that identifies the module containing the assembly manifest. If the specified file cannot be found, the loader will throw a System.FileNotFoundException exception. If the specified file can be found but is not a CLR module containing an assembly manifest, the loader will throw a System.BadImageFormatException exception. Finally, if the CODEBASE is a URL that uses a scheme other than file:, the caller must have WebPermission access rights or else a System.SecurityException exception is thrown. Additionally, assemblies at URLs with protocols other than file: are first downloaded to the download cache prior to being loaded.

Listing 2.2 shows a simple C# program that loads an assembly located at file://C:/usr/bin/xyzzy.dll and then creates an instance of the contained type named AcmeCorp.LOB.Customer. In this example, all that is provided by the caller is the physical location of the assembly.
When a program uses the assembly loader in this fashion, the CLR ignores the four-part name of the assembly, including its version number.

Example 2. 2. Loading an Assembly with an Explicit CODEBASE

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") ;
  }
}

Although loading assemblies by location is somewhat interesting, most assemblies are loaded by name using the assembly resolver. The assembly resolver uses the four-part assembly name to determine which underlying file to load into memory using the assembly loader. As shown in Figure 2.9, this name-to-location resolution process takes into account a variety of factors, including the directory the application is hosted in, versioning policies, and other configuration details (all of which are discussed later in this chapter).

The assembly resolver is exposed to developers via the Load method of the System.Reflection.Assembly class. As shown in Listing 2.3, this method accepts a four-part assembly name (either as a string or as an AssemblyName reference) and superficially appears to be similar to the LoadFrom method exposed by the assembly loader. The similarity is only skin deep because the Load method first uses the assembly resolver to find a suitable file using a fairly complex series of operations. The first of these operations is to apply a version policy to determine exactly which version of the desired assembly should be loaded.

Example 2.3. Loading an Assembly Using the 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") ;
  }
}
清风挽心 2024-11-23 15:20:01

这有点偏离你的问题,但如果你不想按需加载所有程序集,你可以立即加载所有程序集。像这样的事情应该可以解决问题

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();
}

This is a little off your question, but you can load all the assemblies at once if you would prefer not having it happen on demand. Something like this should do the trick

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();
}
遮了一弯 2024-11-23 15:20:01

CLR 根据需要加载程序集。当您执行一个方法时,它会查看它在哪里(哪个模块等),如果未加载,则会加载它。

这是一篇关于 CLR 性能的文章,讨论了加载程序集:

当 CLR 即时 (JIT) 编译 Start 方法时,它需要加载该方法中引用的所有程序集。这意味着异常处理程序中引用的所有程序集都将被加载,即使在应用程序执行的大部分时间可能不需要它们。

这篇文章 适用于 SilverLight,但它确实讨论了 CLR 发生的情况。

The CLR loads the assemblies on demand. When you execute a method, it looks to see where it is (which module etc.), and if it isn't loaded, it loads it.

Here is an article on CLR performance and talks about loading assemblies:

When the CLR just-in-time (JIT) compiles the Start method it needs to load all assemblies referenced within that method. This means that all assemblies referenced in the exception handler will be loaded, even though they might not be needed most of the time the application is executed.

This article is for SilverLight, but it does talk a little about what happens with the CLR.

傾旎 2024-11-23 15:20:01

只要创建相应的 COM 对象,COM DLL 就会按需加载。对于非 COM dll 也可能发生这种情况。

COM DLLS are loaded on demand whenever the corresponding COM object is created. This also could be happening with non COM dlls as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文