乐趣区

CLR Via读书笔记第一章(3)CLR执行程序集的IL代码

在了解 CLR 运行之前让我们先简单了解一下 IL
除了编译器编译的 IL 代码,IL 也是一种汇编语言,也就是说我们可以直接编写 IL 代码,当然也有对应的 IL 编译器,值得一提的是对于面向 CLR 的其他语言,CLR 只开放了一部分功能,而 IL 可以访问 CLR 的全部功能。
前面一章我们介绍了 CLR 的所有初始工作,最后在调用 Main 入口方法的时候,CLR 需要将程序集中的 IL 代码转为 CPU 指令,也就是 CLR 中 JIT(just-in-time)编译器的职责,CLR 会即时编译 IL 代码
即时编译:在运行的时候才会进行编译(类似懒加载)当 CLR 运行并调用方法时做了如下几件事情 1、检测出所有方法中所有被引用的类型,并创建一个内部数据结构进行管理,每个类型的方法都会记录指向名为 JITComplier 函数的地址,2、在方法被调用的时候,函数会在与元数据中查找被调用的方法对应的 IL 代码,对其验证并将代码编译成 CPU 指令 3、将 CPU 指令存贮到动态分配的内存中 4、回到内部数据结构中,修改对应方法记录的地址,指向刚才编译好的 CPU 指令的地址 5、最后函数会回到内存当中去运行 CPU 指令

至此一个方法调用的全部流程就走完了,如果不终止程序(终止会将编译好的 cpu 指令丢弃),那么 CLR 在第二次调用方法时,直接在数据结构中找到对应的内存运行 CPU 指令,省去了上面的 2、3、4 步骤
CLR 的 JIT 编译器以及 C#编译器对本机代码的优化 C# 编译器 :/optimize 关闭 –> 编译出的 IL 代码会包含许多 NOP 指令 (no-operation 空操作) 和跳转执行,vs 就是利用的这些指令提供了调试的功能 /optimize 开启 –> 优化后的代码会更小,程序集也会相应变小,更方便阅读 IL 代码(一般估计不会有人去直接阅读 IL 查找问题吧)
JIT 编译器:在 /optimize 关闭 的情况下:/debug – 关闭(默认) –> 有优化 /debug (+/full/pdbonly) –> 未优化:编译器会生成 PDB 文件帮助编译器查找到局部变量并将 IL 代码映射到源代码方便调试,如果指定的是 /debug : full 开关,编译器还会记录每一条 IL 指令生成的本机指令,但会使用额外的时间和内存在 /optimize 开启的情况下:/debug (-/+/full/pdbonly) –> 有优化

虽然编译器在优化代码的过程中会占用额外的时间和内存,但是在实际运行阶段所带来的收益远远大于这些牺牲,并且性能上远远大于非托管代码,例如:1、JIT 编译器针对不同的 CPU 优化本机代码 2、会根据机器对特定的判断进行代码优化 3、CLR 会根据运行状态对代码评估并重新编译(还未实现)
最后再来简单了解一下 NGen.exe 工具 NGen.exe 是.net framework 提供的工具,它可以将代码提前编译好,这样 JIT 编译器不需要在运行是编译提升性能,但其实这个工具并不是很实用 1、因为 NGen 无法对代码进行最优的优化 –> 因为无法确定 CPU 2、对服务器提升不明显 –> 因为只是在第一次运行时有帮助,后面运行的时间时相等的 3、可能失去同步 –> 如果当前代码与执行环境不符合,那么就会从新用 JIT 编译
至此关于 CLR 如何与程序集工作就完成了,下一节我们将介绍.net Framework 的 Framework 类库以及 CTS CLS

退出移动版