【C#】CLR
-
CLR是如何工作的
借用维基百科上的一副图来描述CLR的运行流程:
从源代码到应用程序执行CLR主要做了以下工作:
-
将源代码编译成托管模块
托管模块是一个标准的 32 位 Microsoft Windows 可移植执行体(PE32)文件,或者是一个标准的 64 位 Windows 可移植执行体(PE32+)文件,它们都需要 CLR 才能执行。一个托管模块主要包含一下几部分信息:
-
PE32货PE32+头
这里描述了当前托管模块的基本信息,包括运行的Windows版本、文件类型、生成时间等。对于包含本地 CPU代码的模块,这个头包含了与本地 CPU 代码有关的信息。
-
CLR头
包含使这个模块成为一个托管模块的信息(可由 CLR 和一些实用程序 进行解释)。头中包含了需要的 CLR 版本,一些 flag,托管模块入口方 法(Main 方法)的 MethodDef 元数据 token,以及模块的元数据、资 源、强名称、一些 flag 以及其他不太重要的数据项的位置/大小
-
元数据
每个托管模块都包含元数据表。主要有两种类型的表:一种类型的表 描述源代码中定义的类型和成员,另一种类型的表描述源代码引用的 类型和成员
-
IL (中间语言)代码
编译器编译源代码时生成的代码。在运行时, CLR 将 IL 编译成本地 CPU 指令
-
-
将托管模块合并成程序集
CLR 实际不和模块一起工作,而是和程序集一起工作的。可以从以下两点对程序集有一个初步的认识:
- 首先,程序集是一个或多个模块/资源文件的逻辑性分组。
- 其次,程序集是重用、安全性以及版本控制的最小单元。
-
加载CLR
在这一步,Windows 检查好 EXE 文件头,决定是创建 32 位、 64 位还是 WoW64 进程之后,会在进程的地址空间中 加载 MSCorEE.dll 的 x86,x64 或 IA64 版本。如果是 Windows 的 x86 版本,MSCorEE.dll 的 x86 版本在 C:\Windows\System32 目录中。如果是 Windows 的 x64 或 IA64 版本,MSCorEE.dll 的 x86 版本在 C:\Windows\SysWow64 目录中,64 位版本(x64 或者 IA64)则在 C:\Windows\System32 目录中(为了向后 兼容)。然后,进程的主线程调用 MSCorEE.dll 中定义的一个方法。这个方法初始化 CLR,加载 EXE 程序集, 然后调用其入口方法(Main)。随即,托管的应用程序将启动并运行。
-
执行程序集中的代码
到目前为止,源代码已经被编译成二进制的IL并且包含在程序集中,而且被CLR加载。但是,直接执行运算的CPU来说二进制的IL还是太高级了,而且不同的CPU支持的指令集也有所差异。因此,CLR在这里还需要对已经编译好的IL再次编译,针对CPU版本生成可以直接运行的CPU指令,这个过程是由JIT(Just In Time)编译器完成的,可以称作“即时编译”。
当第一次执行某个函数时,MSCorEE.dll 的JITCompiler函数会从程序集的元数据中获取该方法和方法的IL,并且分配一块内存地址,然后将IL编译成的本地代码放入这块内存,然后执行内存中的本地代码。
当再次执行这个函数的时候,由于内存中已经存在JIT编译好的本地代码,因此不需要再次进行JIT过程,可以直接执行内存中的本地代码。 可以预知的结果是,这种情况下应用程序启动后第一次调用某个模块所花费的时间要比以后调用这个模块要稍微多一些。
现在,通过本地代码生成技术,已经可以在编译阶段就根据计算机的的实际环境生成本地代码,这样以来就可以在运行时节省JIT编译的时间,提高程序的启动效率。这看起来是一个不错的功能,但是实际上运用的不是很广泛,主要是有一下限制:
- 编译时生成的本地代码太过于依赖本地环境,一旦环境有变化 (包括操作系统更新、.Net Framework版本更新、CPU更换等),以前生成的本地代码都不再适用。
- 编译时生成的本地代码必须要和程序集保持同步。
- 编译时生成的本地代码不能像运行时JIT编译那样根据运行时的情况对代码进行优化。
-
-
CL有哪些功能
这里借用Jeffrey Richter的一段原话:
At first, I thought that the .NET Framework was an abstraction layer over the Win32 API and COM. As I invested more and more of my time
into
it, however, I realized that it was much bigger. In a way, it
is
its own operating system. It has its own memory manager, its own security system, its own file loader, its own error handling mechanism, its own application isolation boundaries (AppDomains), its own threading models, and more.
虽然原文中Jeffrey Richter说的是.Net Framework,但很显然这些功能都是.Net Framework的核心组件CLR来提供的,正是CLR使.Net Framework并不是Win32 API和COM的一个抽象层,而是有了自己的"操作系统"(Jeffrey Richter的意思应该是在一定程度上,虚拟机也可以认为是一个小型的操作系统)。总结起来,CLR主要提供了一下功能:
- 基类库支持 (Base Class Library Support)
- 内存管理 (Memory Management)
- 线程管理 (Thread Management)
- 垃圾回收 (Garbage Collection)
- 安全性 (Security)
- 类型检查 (Type Checker)
- 异常处理 (Exception Manager)
- 即使编译 (JIT)
本系列主要围绕以上功能对CLR进行探讨。