一、前言
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://ilspy.net/
把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 { static void 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代碼如下:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 42 (0x2a) .maxstack 2 .locals init ([0] int32 i, [1] int32 j, [2] int32 k, [3] int32 answer) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: stloc.0 IL_0003: ldc.i4.2 IL_0004: stloc.1 IL_0005: ldc.i4.3 IL_0006: stloc.2 IL_0007: ldloc.0 IL_0008: ldloc.1 IL_0009: add IL_000a: ldloc.2 IL_000b: add IL_000c: stloc.3 IL_000d: ldstr "i+j+k=" IL_0012: ldloc.3 IL_0013: box [mscorlib]System.Int32 IL_0018: call string [mscorlib]System.String::Concat(object, object) IL_001d: call void [mscorlib]System.Console::WriteLine(string) IL_0022: nop IL_0023: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() IL_0028: pop IL_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、理解注釋后的代碼
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint //程序入口 // 代碼大小 42 (0x2a) .maxstack 2 // 計算出計算堆棧的能存幾個值 .locals init ([0] int32 i, [1] int32 j, [2] int32 k, [3] int32 answer) //定義int32類型的i,j,k,answer IL_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: call string [mscorlib]System.String::Concat(object,object) //調用內部方法 IL_001d: call void [mscorlib]System.Console::WriteLine(string) //調用WriteLine IL_0022: nop //無操作 IL_0023: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() //調用ConsoleKey IL_0028: pop //無操作 IL_0029: ret //return } // end of method Program::Main
四、最后
IL是跟我高級架構經理的分享和博客園相關的博客學習總結的,最后留2個題目,也是我的高級架構經理分享中提到的兩個好玩的問題,也能看出對IL的掌握的程度
答案后續公布。