.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的介入时机:

图1 JIT工作时机
JIT是运行时的一个重要职责模块,它将IL转换为本地CPU指令,从上图可以看出,也许你不敢相信,即时编译这个过程是在运行时发生的,这会不会对性能产生影响呢?事实上答案是虽然是肯定的,但这种开销物有所值:
- JIT所造成的性能开销并不显著。
- JIT遵循计算机体系理论中两个经典理论:局部性原理与8020原则。局部性原理指出,程序总是趋向于使用最近使用过的数据和指令,这包括空间的和时间的,将局部性原理引申可以得出,程序总是趋向于使用最近使用过的数据和指令,以及这些正在使用的数据和指令临近的数据和指令(凭印象写的,但不曲解原意);而8020原则指出,系统大多数时间总是花费80%的时间去执行那20%的代码。 根据这两个原则,JIT在运行时会实时的向前、后优化代码,这样的工作只有在运行时才可以做到。
- JIT只编译需要的那一段代码,而不是全部,这样节约了不必要的内存开销。
- JIT会根据运行时环境,即时的优化IL代码,即同样的IL代码运行在不同CPU上,JIT编译出的本地代码是不同的,这些不同代码面向自己的CPU做出了优化。
- 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编译),根据这些地址,就可以找到对应的方法实现。
未完待续
转自:http://www.cnblogs.com/isline/archive/2009/12/22/1629831.html
.Net Discovery 系列之五--深入浅出.Net实时编译机制(上)的更多相关文章
- .Net Discovery 系列之六--深入浅出.Net实时编译机制(下)
接上文 在初始化时,HashTable中各个方法指向的并不是对应的内存入口地址,而是一个JIT预编译代理,这个函数负责将方法编译为本地代码.注意,这里JIT还没有进行编译,只是建立了方法表! 下表(表 ...
- .Net Discovery系列之三 深入理解.Net垃圾收集机制(上)
前言: 组成.Net平台一个很重要的部分----垃圾收集器(Garbage Collection),今天我们就来讲讲它.想想看没有GC,.Net还能称之为一个平台吗?各种语言虽然都被编译成MSIL,但 ...
- .Net Discovery 系列之七--深入理解.Net垃圾收集机制(拾贝篇)
关于.Net垃圾收集器(Garbage Collection),Aicken已经在“.Net Discovery 系列”文章中有2篇的涉及,这一篇文章是对上2篇文章的补充,关于“.Net Discov ...
- .Net Discovery系列之十-深入理解平台机制与性能影响(上)
转眼间<.Net Discovery>系列文章已经推出1年了,本文为该系列的第10-13篇文章,在本文中将对以前所讲的.Net平台知识做一个小小的总结与机制分析,引出并重点介绍这些机制对程 ...
- .Net Discovery系列之十一-深入理解平台机制与性能影响 (中)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马—JIT. 有关JIT的机制分析 ● 机制分析 以C#为例, ...
- .Net Discovery 系列之一--string从入门到精通(上)
string是一种很特殊的数据类型,它既是基元类型又是引用类型,在编译以及运行时,.Net都对它做了一些优化工作,正式这些优化工作有时会迷惑编程人员,使string看起来难以琢磨,这篇文章分上下两章, ...
- .Net Discovery系列之四 深入理解.Net垃圾收集机制(下)
上一节给大家介绍了 .Net GC的运行机制,下面来讲下与GC相关的重要方法. 第二节.GC关键方法解析 1.Dispose()方法 Dispose可用于释放所有资源,包括托管的和非托管的,需要自己实 ...
- .Net Discovery系列之十二-深入理解平台机制与性能影响(下)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制.即时编译机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的异常捕获机制与字符串驻留机制. 三.关于异常捕获机制 虽然我们已经很 ...
- Red Gate系列之五 .NET Reflector 7.6.1.824 Edition .NET程序反编译神器(附插件安装教程2012-10-13更新) 完全破解+使用教程
原文:Red Gate系列之五 .NET Reflector 7.6.1.824 Edition .NET程序反编译神器(附插件安装教程2012-10-13更新) 完全破解+使用教程 Red Gate ...
随机推荐
- JDK1.8源码Collections
正文: 一.概述: 此类完全由在 collection 上进行操作或返回 collection 的静态方法组成.它包含在 collection 上操作的多态算法,即“包装器”,包装器返回由指定 col ...
- shell-拷贝指定目录外其他全部目录
shopt -s extglob if [ ! -d "desdir" ]; then mkdir desdir fi cp -r Oozie/!(.svn*) desdir/
- Linux USB Host-Controller的初始化代码框架分析【转】
转自:http://blog.csdn.net/zkami/article/details/2496770 usb_hcd_omap_probe (const struct hc_driver *dr ...
- SharePoint 2013 另一个程序正在使用此文件,进程无法访问。 (异常来自 HRESULT:0x80070020)
环境:SharePoint 2013 + Windows Server 2012 R2 在管理中心新建一个Web Application,端口为:88.顺利创建网站集后,打开访问却提示:无法显示此页 ...
- @PostContruct注解
@PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法.
- axios笔记
参考:http://www.cnblogs.com/Upton/p/6180512.html https://cloud.tencent.com/developer/article/1098141 ...
- Ibatis.Net 动态SQL语句学习(六)
在学习动态SQL语句之前,首先必须对条件查询有一定了解,先来学习如何向IBatis.Net的映射文件里传入参数吧. 一.条件查询 1.传递单个参数 如根据Id查询: <select id=&qu ...
- springMVC:将controller中数据传递到jsp页面
1> 将方法的返回值该为ModelAndView在返回时,将数据存储在ModelAndView对象中如: newModelAndView("/WEBINF/jsp/showData.j ...
- LINQ基本语句
一.什么是LINQ 1.定义:LINQ是Language Integrate Query的缩写,它在对象和数据之间建立一种对应关系,可以使用访问内存对象的方式查询数据集合. 2.特点:由于LINQ中查 ...
- day--14前端(HTML、CSS)
浏览器相当于客户端,浏览器访问服务端,收到消息之后里面断开,一次请求,一次响应,一次断开. Web框架本质 http://www.cnblogs.com/wupeiqi/articles ...