分享

详解.NET IL代码

 走出尘埃 2016-12-19

一、前言

IL是什么?

 Intermediate Language (IL)微软中间语言

C#代码编译过程?

C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令;

JIT编译器把IL代码转为机器识别的机器代码。如下图

语言编译器:无论是VB code还是C# code都会被Language Compiler转换为MSIL

MSIL的作用:MSIL包含一些元数据和中间语言指令

JIT编译器的作用:根据系统环境将MSIL中间语言指令转换为机器码

为什么ASP.NET网站第一次运行时会较慢,而后面的执行速度则会相对快很多?

当你第一次运行.NET开发的站点时,CLR会将MSIL通过JIT进行编译,最终转换为执行速度非常快的Native Code。这可以解释。

为什么要了解IL代码?

如果想学好.NET,IL是必须的基础,IL代码是.NET运行的基础,当我们对运行结果有异议的时候,可以通过IL代码透过表面看本质;

IL也是更好理解、认识CLR的基础;

大量的实例分析是以IL为基础的,所以了解IL,是读懂他人代码的必备基础,同时自己也可以获得潜移默化的提高;

二、如何把ILDasm导入到VS中

想要看IL代码需要使用ILDasm工具,工具一般在电脑的

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\ildasm.exe

也可以下载ILSpy:http:///

把ILDasm导入到VS工具中,使用方便,具体如下:工具 - > 外部工具 

导入之后,vs工具里面就有ILDasm工具了。以后想看IL代码方便多了。

IL代码通过ILDasm反编译后(左图),ILDasm图标意义(右图)

三、分析IL代码

在分析IL代码之前,要先理解几个概念:

图片来源:https://msdn.microsoft.com/zh-tw/library/dd229210.aspx

Managed Heap(托管堆):这就是NET中的托管堆,用来存放引用类型,它是由GC(垃圾回收器自动进行回收)管理;

Call Stack(调用堆栈):调用堆栈:调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法。

Evaluation Stack(计算堆栈):每个线程都有自己的线程栈,IL 里面的任何计算,都发生在 Evaluation Stack 上,其实就是一个 Stack 结构。可以 Push,也可以 Pop。

可以对照IL指令:指令列表,一步一步来分析IL代码

1、用C#写一个简单控制台应用程序

using System;namespace ILDemo{ class Program { staticvoid Main(string[] args) { int i = 1; int j = 2; int k = 3; int answer = i + j + k; Console.WriteLine('i+j+k=' + answer); Console.ReadKey(); } }}

2、 用ILDasm打开bin下的.exe文件查看代码,具体IL代码如下:

.methodprivatehidebysig static void Main(string[] args) cilmanaged{ .entrypoint // 代码大小 42 (0x2a).maxstack2.localsinit ([0] int32 i, [1] int32 j, [2] int32 k, [3] int32 answer) IL_0000:nopIL_0001:ldc.i4.1IL_0002:stloc.0IL_0003:ldc.i4.2IL_0004:stloc.1IL_0005:ldc.i4.3IL_0006:stloc.2IL_0007:ldloc.0IL_0008:ldloc.1IL_0009:addIL_000a:ldloc.2IL_000b:addIL_000c:stloc.3IL_000d:ldstr'i+j+k='IL_0012:ldloc.3IL_0013:box [mscorlib]System.Int32 IL_0018:callstring [mscorlib]System.String::Concat(object, object) IL_001d:callvoid [mscorlib]System.Console::WriteLine(string) IL_0022:nopIL_0023:call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_0028:popIL_0029:ret} // end of method Program::Main

3、会用到的IL指令:

nop:无操作

ret:从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。

call:调用由传递的方法说明符指示的方法。

box:将值类转换为对象引用,就是装箱,同理可以知道拆箱unbox

ldc.i4.X:把int32的值推送到计算堆栈

stloc.X:把计算堆栈顶部的值放到调用堆栈索引为X处

ldloc.X:把调用堆栈X处的值复制到计算堆栈

4、理解注释后的代码

.methodprivatehidebysig static void Main(string[] args) cilmanaged{ .entrypoint//程序入口// 代码大小 42 (0x2a).maxstack2// 计算出计算堆栈的能存几个值.localsinit ([0] int32 i, [1] int32 j, [2] int32 k, [3] int32 answer) //定义int32类型的i,j,k,answerIL_0000:nop//无操作IL_0001:ldc.i4.1//把i的值放到计算堆栈上IL_0002:stloc.0//把计算堆栈顶部的值(i的值)放到调用堆栈索引0处IL_0003:ldc.i4.2//把j的值放到计算堆栈上IL_0004:stloc.1//把计算堆栈顶部的值(j的值)放到调用堆栈索引1处IL_0005:ldc.i4.3//把k的值放到计算堆栈上IL_0006:stloc.2//把计算堆栈顶部的值(k的值)放到调用堆栈索引2处IL_0007:ldloc.0//把调用堆栈索引为0处的值复制到计算堆栈 IL_0008:ldloc.1//把调用堆栈索引为1处的值复制到计算堆栈IL_0009:add//相加IL_000a:ldloc.2//把调用堆栈索引为2处的值复制到计算堆栈IL_000b:add//相加IL_000c:stloc.3//把计算堆栈顶部的值(add的值)放到调用堆栈索引3处IL_000d:ldstr'i+j+k='//推送对元数据中存储的字符串的新对象引用。IL_0012:ldloc.3//把调用堆栈索引为3处的值复制到计算堆栈IL_0013:box [mscorlib]System.Int32 //装箱IL_0018:callstring [mscorlib]System.String::Concat(object,object) //调用内部方法IL_001d:callvoid [mscorlib]System.Console::WriteLine(string) //调用WriteLineIL_0022:nop//无操作IL_0023:call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() //调用ConsoleKeyIL_0028:pop//无操作IL_0029:ret//return} // end of method Program::Main

四、最后

IL是跟我高级架构经理的分享和博客园相关的博客学习总结的,最后留2个题目,也是我的高级架构经理分享中提到的两个好玩的问题,也能看出对IL的掌握的程度

 答案后续公布。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多