.Net Discovery 系列之六--深入浅出.Net实时编译机制(下)
接上文
在初始化时,HashTable中各个方法指向的并不是对应的内存入口地址,而是一个JIT预编译代理,这个函数负责将方法编译为本地代码。注意,这里JIT还没有进行编译,只是建立了方法表!
下表(表1)为首次加载调用时HashTable的情况:
表1 方法表示意
|
方法槽 |
方法描述 |
|
a1() |
PreJitStub |
|
a2() |
PreJitStub |
|
a3() |
PreJitStub |
好了有了这个HashTable后,JIT开始编译第一个被调用的方法A.a1("First"),这是由一个JIT内部函数来完成的(上面提到的),遗憾的事,目前还没有发现介绍这个函数的相关资料,有些书中称它为“JIT编译者”,那本文也这么称呼它吧。
下图为首次调用方法时的示意图:

图2 触发JIT编译
JIT借助元数据和IL生成被调用方法的本地代码后,会将这些代码缓存在动态内存中,然后修改HashTable中对应方法的入口地址,将其修改为本地代码的内存片地址(如表2所示),并将这个地址返回给CLR经行执行,A.a1("First")执行完毕,代码继续运行。
运行至A.a1("Second ")时,会直接执行A.a1()方法的内存代码,不会进行再次编译,表2 为再次加载时HashTable的情况。
表2 方法表变化
|
方法槽 |
方法描述 |
|
a1() |
XXXXXXXXX内存地址 |
|
a2() |
PreJitStub |
|
a3() |
PreJitStub |
再次加载流程示意图:

图3 未触发JIT编译

图4 方法表、方法描述、预编译代理关系
图2中所示的MS核心引擎指的是一个叫做MSCorEE的DLL,即Microsoft .NET Runtime Execution Engine,它是一个桥接DLL,连同mscorwks.dll主要完成以下工作:
- 查找程序集中包含的对应类型清单,并调用元数据遍历出包含的方法。
- 结合元数据获得这个方法的IL。
- 分配内存。
- 编译IL为本地代码,并保存在第3步所分配的内存中。
- 将类型表(就是指上文中提到的HashTable)中方法地址修改为第3步所分配的内存地址。
- 跳转至本地代码中执行。
所以随着程序的运行时间增加,越来越多的方法的IL被编译为本地代码,JIT的调用次数也会不断减少。
下面借助WinDbg来证实以上的说法,示例中的源程序可以到这里下载到:
http://files.cnblogs.com/isline/IsLine.JITTester.rar

namespace JITTester
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void GO_Click(object sender, EventArgs e)
{
new A().a1();
lb_msg.Text = "调用完毕!";
}
}
class A
{
public void a1() { }
public C a2 = new C();
}
class B
{
public void b1() { }
public void b2() { }
}
class C
{
public void c1() { }
public void c2() { }
}
}

代码中定义了3个类,分别为A、B、C,在“GO”按钮按下后,将调用类型A中的a1()方法,而Form1_Load 中什么也不做,目的是程序运行后,在空载的情况下查看方法描述对应地址入口的情况。
好,第一步运行JITTester.exe程序,并打开WinDbg附加这个进程

图 5 附件进程
第二步,附加进程成功后,在WinDbg中加载SOS.dll

图6 加载SOS.dll
第三步,使用name2ee命令遍历所有已加载模块,name2ee格式为name2ee *! [程序集].[类型]

图7 查看类型信息
回车后注意高亮区域的信息:

图8 JIT前A类型的信息
高亮区域显示的是“<not loaded yet>”,这说明虽然运行和程序,但未点击按钮时,A类型未被JIT,因为它还没有入口地址。这一点体现了即时、按需编译的思想。
同样,!name2ee *!JITTester.B和!name2ee *!JITTester.C命令会得到同样的结果。
好,现在做第4步操作,Detach Debuggee进程,并回到程序中点击“GO”按钮

图9 点击按钮
第五步 重新附加进程(参考第一步),这时程序已经调用了new A().a1()方法,并重新执行命令!name2ee *!JITTester.A ,注意高亮部分

图10 JIT后A类型的信息
和图8中的信息比较,图10中的方法表地址已经变为JIT后的内存地址,这时图4中的Stub槽将被一条强制跳转语句替换,跳转目标与该地址有关。这一点说明JIT在大多情况下,只编译一次代码。
同样命令查看B类型:

图11 JIT后B类型的信息
该类型未被调用,所以还未被JIT。
C类型:

图12 JIT后C类型的信息
由于实例化A类型时和C类型相关,所以C类型已经JIT了。
第三节.Native Image Generator
Native Image Generator中文译为本地代码生成器,我更习惯叫它“本地映像”,因为通过工具NGen.exe生成的本地代码是无法部分载入的,这意味着操作系统会加载整个程序集文件。
上一节中提到过,有两种方法可以获得本地代码,JIT方式和Native Image Generator方式,JIT方式是在运行时动态编译需要的代码,而NGen.exe会创建托管程序集的本机映像,并且将该映像安装到GAC中,运行该程序集时,就会自动使用该本机映像而不是JIT它们。
这听起来似乎很美妙,但是你必须做好以下准备:
- 当FrameWork版本、CPU类型、操作系统版本发生变化时,.Net会恢复JIT机制。
- NGen.exe工具并不能避免发布IL,事实上,即使使用NGen.exe工具,CLR依然会使用到元数据和IL。
- 忽略了局部性原理(上一节中提到的),系统会加载整个映像文件到内存中,并很可能重定位文件,修正内存地址引用。
- NGen.exe生成的代码无法在运行时进行优化,无法直接访问静态资源,也无法在应用程序域之间共享程序集。
此外,JIT不但有编译的本事,还会根据内存资源情况换出使用率低的代码,节省资源,这对于一些基于.Net平台的电子产品是很重要的。
所以,除非你已十分清楚程序性能是由于首次编译造成的性能问题,否则尽量不要人工生成本地代码。
转自:http://www.cnblogs.com/isline/archive/2009/12/27/1633453.html
.Net Discovery 系列之六--深入浅出.Net实时编译机制(下)的更多相关文章
- .Net Discovery 系列之五--深入浅出.Net实时编译机制(上)
欢迎阅读“.Net Discovery 系列”文章,本文将分上.下两部分为大家讲解.Net JIT方面的知识,敬请雅正. JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制, ...
- .Net Discovery系列之四 深入理解.Net垃圾收集机制(下)
上一节给大家介绍了 .Net GC的运行机制,下面来讲下与GC相关的重要方法. 第二节.GC关键方法解析 1.Dispose()方法 Dispose可用于释放所有资源,包括托管的和非托管的,需要自己实 ...
- .Net Discovery系列之十-深入理解平台机制与性能影响(上)
转眼间<.Net Discovery>系列文章已经推出1年了,本文为该系列的第10-13篇文章,在本文中将对以前所讲的.Net平台知识做一个小小的总结与机制分析,引出并重点介绍这些机制对程 ...
- .Net Discovery 系列之七--深入理解.Net垃圾收集机制(拾贝篇)
关于.Net垃圾收集器(Garbage Collection),Aicken已经在“.Net Discovery 系列”文章中有2篇的涉及,这一篇文章是对上2篇文章的补充,关于“.Net Discov ...
- .Net Discovery系列之十一-深入理解平台机制与性能影响 (中)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马—JIT. 有关JIT的机制分析 ● 机制分析 以C#为例, ...
- .Net Discovery系列之三 深入理解.Net垃圾收集机制(上)
前言: 组成.Net平台一个很重要的部分----垃圾收集器(Garbage Collection),今天我们就来讲讲它.想想看没有GC,.Net还能称之为一个平台吗?各种语言虽然都被编译成MSIL,但 ...
- .Net Discovery系列之十二-深入理解平台机制与性能影响(下)
上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制.即时编译机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的异常捕获机制与字符串驻留机制. 三.关于异常捕获机制 虽然我们已经很 ...
- Red Gate系列之六 SQL Test 1.0.12.3 Edition SQL测试工具 完全破解+使用教程
原文:Red Gate系列之六 SQL Test 1.0.12.3 Edition SQL测试工具 完全破解+使用教程 Red Gate系列之六 SQL Test 1.0.12.3 Edition S ...
- 单元测试系列之六:JUnit5 技术前瞻
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6868495.html JUnit ...
随机推荐
- [cookie篇]cookie-parser之parser.js
cookie-parser的作用,官方的说法是:Parse Cookie header and populate req.cookies with an object keyed by the coo ...
- nor flash 和 nand flash
NOR Flash是很常见的一种存储芯片,数据掉电不会丢失,支持Execut On Chip,即程序可以直接在FLASH片内执行(这意味着存储在NOR FLash上的程序不需要复制到RAM就可以直接运 ...
- KVM -> 虚拟化简介&虚拟机安装_01
什么是虚拟化? 在计算机技术中,虚拟化(技术)或虚拟技术(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源(CPU.内存.磁盘空间.网络适配器等),予以抽象.转换后呈现 ...
- weblogic控制台部署web应用
如何使用weblogic管理控制台部署和卸载一个WEB应用呢?下面我们来分步演示! 工具/原料 Oracle WebLogic WEB应用War包 方法/步骤 1 用IE浏览器,打开管理控制台 ...
- mysql的完整卸载
一.卸载MySQL数据库 1.检查mysql服务并关闭服务进程 (1)登录Linux后执行service mysqld status 或者service mysql status命令查看MySQL服务 ...
- Linix下修改mysql服务器编码
1. 找到mysql的配置文件,拷贝到etc目录下,第一步很重要 把/usr/share/doc/mysql-server-5.1.52/my-large.cnf 复制到 /etc/my.cnf 即用 ...
- jQuery预览图
- CSS------给字体添加边框时,边框大小无法改变问题
如图: 代码:(需要将display属性设置为inline-block,在设置height和line-height调整位置) //品牌点击 $(".li-brand").click ...
- ref:web security最新学习资料收集
ref:https://chybeta.github.io/2017/08/19/Web-Security-Learning/ ref:https://github.com/CHYbeta/Web-S ...
- P1679 神奇的四次方数
P1679 神奇的四次方数用一些什么东西组成一个什么东西,要求什么东西最优,这时候要考虑背包,不过要分析清楚是什么类型的背包.这题显然是个完全背包. #include<iostream> ...