.Net Discovery 系列之五--深入浅出.Net实时编译机制(上)

简介:

  欢迎阅读“.Net Discovery 系列”文章,本文将分上、下两部分为大家讲解.Net JIT方面的知识,敬请雅正。

 

    JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制,这种机制的命名来源于丰田汽车在20世纪60年代实行的一种生产方式,中文译为“准时制”。

    .Net 的JIT编译器在设计初衷和运行方式来上讲,都与丰田汽车的这种“准时生产”思想体系有着很大的相似之处,所以让我们先来透过“准时生产”方式来理解.Net的JIT机制吧。

    “准时生产”的基本思想可概括为“在需要的时候,按需要的量生产所需的产品”,这正是.Net JIT编译器的设计初衷,即在需要的时候编译需要的代码。

 

    第一节.Me JIT

    以C#为例,在C#代码运行前,一般会经过两次编译,第一阶段是C#代码向MSIL的编译,第二阶段是IL向本地代码的编译。第一阶段的编译成果是生成托管模块,第二阶段的编译成果是生成本地代码以供运行,从这里各位同学可以看出,第一阶段生成的MSIL是不能直接运行的。

这里先要解释一下什么是MSIL和托管模块。

    MSIL:

    MSIL 全称为Microsoft Intermediate Language,中文译为“微软中间语言”,它是一种介于高级语言和汇编语言之间的伪汇编语言(姑且这么叫,各位有不同意见的同学不必激动)。当用户编译运行一个.NET程序时,高级语言编译器会将源代码翻译成一组可以独立于CPU的指令。

    可以看出IL 包括用于加载(ldstr )、存储(压栈、弹栈)和初始化对象(locals)以及调用对象方法(call)的指令,还包括用于算术和逻辑运算、控制流、直接内存访问、异常处理和其他操作的指令。

    C#代码:

 
 
string str_test = " test " ;
System.String Str_test
= " test " ;


对应IL码:   

复制代码
 
 
// 代码大小 14 (0xe)
     .maxstack 1
.locals init ([
0 ] string str_test,
[
1 ] string Str_test)
IL_0000: nop
IL_0001: ldstr
" test "
IL_0006: stloc.
0
IL_0007: ldstr
" test "
IL_000c: stloc.
1
IL_000d: ret
复制代码

    托管模块:

    托管模块(managed module)是一个标准32位或64位Microsoft  Windows可移植可执行体(PE32或PE32+)文件,托管模块需要CLR才能执行,它包含了上面介绍的IL代码,还包含元数据、PE头、CLR头几部分。

    元数据(metadata)可以理解为一个HashTable,Table中映射了内置类型和成员以及引用的类型和成员,这些类型与成员供IL使用,所以元数据总是需要关联对应的IL代码,编译器也是同时生成元数据与IL,以保证自描述的同步。

    PE头(Portable Executable,中文译为可移植的可执行的)包括了PE32与PE32+,标示了托管模块的运行环境以及JIT优化本地代码时所要用到的信息,这在后面会讲到。

    CLR头主要包括方法的入口地址标记,以及资源、强命名等信息,这些信息是GAC重要的参数依据。

    下图可以表示出JIT的介入时机:

 2009122209503969.jpg

图1 JIT工作时机

    JIT是运行时的一个重要职责模块,它将IL转换为本地CPU指令,从上图可以看出,也许你不敢相信,即时编译这个过程是在运行时发生的,这会不会对性能产生影响呢?事实上答案是虽然是肯定的,但这种开销物有所值:

  1. JIT所造成的性能开销并不显著。
  2. JIT遵循计算机体系理论中两个经典理论:局部性原理与8020原则。局部性原理指出,程序总是趋向于使用最近使用过的数据和指令,这包括空间的和时间的,将局部性原理引申可以得出,程序总是趋向于使用最近使用过的数据和指令,以及这些正在使用的数据和指令临近的数据和指令(凭印象写的,但不曲解原意);而8020原则指出,系统大多数时间总是花费80%的时间去执行那20%的代码。   根据这两个原则,JIT在运行时会实时的向前、后优化代码,这样的工作只有在运行时才可以做到。
  3. JIT只编译需要的那一段代码,而不是全部,这样节约了不必要的内存开销。
  4. JIT会根据运行时环境,即时的优化IL代码,即同样的IL代码运行在不同CPU上,JIT编译出的本地代码是不同的,这些不同代码面向自己的CPU做出了优化。
  5. JIT会对代码的运行情况进行检测,并对那些特殊的代码经行重新编译,在运行过程中不断优化。

    实际上JIT的优点还不止如此,它对内联、策略引擎(.Net Discovery 系列之四--深入理解.Net垃圾收集机制(下)  中包含对策略引擎的描述)、CLR反馈、代码回收(非垃圾回收,这在第二节中会有介绍)等方面都会有不可磨灭的贡献。

    必须指出的是JIT在第一次编译IL后,会修改对应方法相应的内存地址入口(绕口啊~~),下一次需要执行这个方法时,CLR会直接访问对应的内存地址,而不会经过JIT了。

 

   第二节.编译与执行

    在上一节中我们讨论了与JIT相关的一些元素和JIT的优势,这一节将为大家重点介绍JIT在编译方面的原理。

    C#等高级语言必须被编译为IL才可被执行,IL在执行前必须被便以为本地代码才可运行,这里有两种方法可以获得本地代码,JIT方式和Native Image Generator方式,本节主要讨论JIT方式。

    必须指出的是JIT在第一次编译IL后,会修改对应方法相应的内存地址入口,下一次需要执行这个方法时,CLR会直接访问对应的内存地址,而不会经过JIT了,这样无疑加快了程序运行的速度,这是怎样的一个过程呢?

    以Load()方法为例,假如Load()方法中调用了两次同类型中的方法:

 

复制代码
 
 
Void Load()

{

A.a1(
" First " );

A.a1(
" Second " );

}
static class A

{

Public
void a1( string str){}

Public
void a2( string str){}

Public
void a3( string str){}

}

复制代码

 

    运行时,操作系统会根据托管模块中各种头信息,装载相应的运行时框架,Load()被加载,由于是第一次加载,这会触发对Load()的即时编译,JIT会检测Load()中引用的所有类型,并结合元数据遍历这些类型中定义的所有方法实现,并用一个特殊的HashTable(仅用于理解)储存这些类型方法与其对应的入口地址(在未被JIT前,这个入口地址为一个预编译代理(PreJitStub),这个代理负责触发JIT编译),根据这些地址,就可以找到对应的方法实现。


本文转自Aicken(李鸣)博客园博客,原文链接:http://www.cnblogs.com/isline/archive/2009/12/22/1629831.html,如需转载请自行联系原作者

相关文章
|
4月前
|
小程序 安全 JavaScript
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
|
安全 JavaScript API
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
188 0
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
|
存储 开发框架 Java
【CLR C#】浅谈.Net的GC(垃圾回收)机制及其整体流程
在.NET程序开发中,为了将开发人员从繁琐的内存管理中解脱出来,将更多的精力花费在业务逻辑上,CLR提供了自动执行垃圾回收的机制来进行内存管理,开发人员甚至感觉不到这一过程的存在。.NET程序可以找出某个时间点上哪些已分配的内存空间没有被程序使用,并自动释放它们。自动找出并释放不再使用的内存空间机制,就称为垃圾回收机制。本文主要介绍.Net中的GC(垃圾回收)机制及其整体流程。
【CLR C#】浅谈.Net的GC(垃圾回收)机制及其整体流程
|
缓存 运维 安全
『容错机制』 .NET 弹性和瞬态故障处理库Polly应用详解
所谓瞬态故障,就是说故障不是必然会发生的,而是偶然可能会发生的,比如网络偶尔会突然出现不稳定或无法访问这种故障。至于弹性,就是指应对故障 Polly 的处理策略具有多样性和灵活性,它的各种策略可以灵活地定义和组合。
368 1
『容错机制』 .NET 弹性和瞬态故障处理库Polly应用详解
|
物联网 Linux KVM
X86模拟龙芯与编译.NET CoreCLR
X86模拟龙芯与编译.NET CoreCLR
359 0
X86模拟龙芯与编译.NET CoreCLR
|
开发框架 监控 .NET
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(四)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
96 0
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(四)
|
XML 存储 JSON
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(三)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
115 0
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(三)
|
JSON 开发框架 .NET
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(二)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
92 0
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(二)
|
JSON 开发框架 .NET
ASP.NET Core : 二十三. 深入聊一聊配置的内部处理机制(一)
上一章介绍了配置的多种数据源被注册、加载和获取的过程,本节看一下这个过程系统是如何实现的。
159 0
|
JSON 开发框架 .NET
ASP.NET Core: 二十. Action的多种数据返回格式处理机制(四)
上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去。
412 0