温故而知新
CLR功能
JIT(just-in-time 运行时编译技术)
通常,程序有两种运行方式:静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码,而解释执行的则是一句一句边运行边翻译。
即时编译器
则混合了这二者,一句一句编译源代码,但是会将翻译过的代码缓存起来以降低性能损耗。相对于静态编译代码,即时编译的代码可以处理延迟绑定并增强安全性。即时编译器有两种类型,一是字节码翻译,二是动态编译翻译。
为了执行方法,首先必须将方法的IL转换成本机(native)CPU指令。这是CLR的JIT编译器的职责。
流程讲解
- 在Main方法执行之前,CLR会检测出Main的
代码引用
的所有类型.- CLR分配一个
内部数据结构
来管理对引用类型的访问. - 图中,Main方法引用了Console类型,CLR分配了一个
内部结构
. - Console类型定义的每个方法都在
内部结构
中存在一个记录项 entry
. - 根据这个记录项可以找到方法的实现.
- 对这个
内部结构
进行初始化,每个方法都指向包含在CLR内部的一个未编档函数
,称之为JITCompiler
.
- CLR分配一个
- 进入Main方法,首次调用
WriteLine方法
时,JITCompiler函数
被调用. JITCompiler
会在定义该类型的程序集的元数据中查找被调用方法的IL代码
.- 然后
JITCompiler
验证IL代码
,将WriteLine方法
的IL代码
编译成本机CPU指令
. 本机CPU指令
保存到动态分配的内存块中.JITCompiler
将在CLR为类型创建的内部数据结构
中,修改引用.- 指向到刚才动态分配的内存块地址. 最后
JITCompiler
跳转到内存块中的代码(WriteLine方法
的具体实现). - 返回到Main中的代码.
- 第二次调用
WriteLine方法
,由于已经进行了验证和编译,会直接跳转到已经编译好的本机CPU指令
代码块中执行.
方法仅在首次调用时才会有一些性能损失,之后调用全是以本机代码的形式全速运行.
JIT 编译技术特点
- JIT编译器将本机CPU指令存储到动态内存中.意味着一旦应用程序终止,编译好的代码也会被丢弃.
JIT编译器会对
本机代码
进行优化.类似于C++编译器后端所做的事情. 可能花较多的时间生成优化代码.在Visual Studio中新建C#项目时,”调试“(Debug)配置指定的是/optimize-/debug:full,而”发布“(release)配置指定的是/optimize+/debug:pdbonly。
JIT能够针对本机CPU为IL代码生成指令,以利用本机指定CPU的任何特殊指令进行编译。相反,非托管应用程序通常是针对具有最小功能集合的CPU编译的。
JIT编译器能够判断一个特定的测试在它运行的机器上是否总是失败,例如,假定有一个方法包含以下代码
// 如果主机只有一个CPU,JIT编译器不会为此代码生成任何CPU指令。
if(numberofCPUs>1){
...//Do something
}
将IL代码编译成本机代码的验证过程
将IL代码编译成本机代码的过程中,CLR会执行验证过程,根据元数据检查类型,返回值,返回语句,参数是否正确等等.
NGen.exe生成的本机代码与JIT生成的本机代码对比
- NGen生成的代码不会像JIT编译器生成的代码进行高度优化.较差的执行时性能.
- CLR加载程序集时,会检查是否存在一个对应的,由NGen生产的本机文件.
- NGen.exe生成的本机文件没有知识产权保护.
- NGen生成的文件可能失去同步.版本不匹配,系统升级过等,就无法使用.
基类库支持功能
.Net Framework 包含 Framework类库(Framework Class Library,FCL).
- FCL是一组DLL程序集的统称.