最近在看CLR Via C#,接触了一些CLR原理方面的问题,总结了一下。
程序集同时包含元数据和中间语言代码(IL),可以通过IL反编译工具看到一个程序集中包含的具体内容。
例如如下指令运行是CLR会这样处理:
static void main() {
Console.WriteLine("Hello");
Console.WriteLine("GoodBye");
}
Main方法首次调用WriteLine时,会调用JITCompiler函数,JITCompiler函数负责将一个方法的IL代码编译成本地CPU指令。
- JIT Compiler首先要找到要调用那个方法,以及具体是什么类型定义了该方法:In the assembly that implement the type(console), lookup the method(writeline) being called in the metadata.
- JIT Compiler会在定义程序集的元数据中搜索被调用方法的IL:From the Metadata, get the IL for this method.
- JIT Compiler为本地CPU指令动态分布一个内存块:allocate a block of memory
- JIT Compiler把IL代码编译成本地CPU指令: Compile the IL into native CPU instructions, the native code is saved in the memory allocated in step3
- JIT Compiler回到内部数据结构,找到与被调用的方法对于的那一条记录,然后将最初调用它的那个引用替换成内存块,Modify the method's entry in the Type's table so that it now points to the memory block allocated in step3
- 最后JIT Compiler 会跳转到内存块中的代码Jump to the native code contained inside the memory block
第二次调用writeLine,这一次由于之前已经对writeLine编译过了,所以会直接执行内存块中的代码,完全跳过JIT Compiler。
注意:
- 这里有一个地址替换的过程,从方法的实现的地址中找出IL,把IL编译为CPU指令放入内存块中,再去替换方法实现的地址,这样实现的地址就指向了CPU指令。
- JIT编译器将本地CPU指令保存在动态内存中,一旦应用程序终止,变越好的代码也会被丢弃。所以如果将来再次运行应用程序或者同时启动了应用程序的两个实例JIT编译器必须再次将IL编译成本地指令。