佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > 一步步教你讀懂NET中IL(圖文詳解)

一步步教你讀懂NET中IL(圖文詳解)

熱門(mén)標(biāo)簽:電銷(xiāo)業(yè)務(wù) 百度AI接口 國(guó)美全國(guó)運(yùn)營(yíng)中心 科大訊飛語(yǔ)音識(shí)別系統(tǒng) 客戶(hù)服務(wù) 人工智能 電商新玩法 網(wǎng)站排名優(yōu)化

接觸NET也有1年左右的時(shí)間了,NET的內(nèi)部實(shí)現(xiàn)對(duì)我產(chǎn)生了很大的吸引力。個(gè)人覺(jué)得:能對(duì)這些底部的實(shí)現(xiàn)進(jìn)行了解和熟練的話,對(duì)以后自己寫(xiě)代碼是有很大幫助的,好了,廢話不多說(shuō),請(qǐng)看下邊:

.NET CLR 和 Java VM 都是堆疊式虛擬機(jī)器(Stack-Based VM),也就是說(shuō),它們的指令集(Instruction Set)都是採(cǎi)用堆疊運(yùn)算的方式:執(zhí)行時(shí)的資料都是先放在堆疊中,再進(jìn)行運(yùn)算。JavaVM 有約 200 個(gè)指令(Instruction),每個(gè)指令都是 1 byte 的 opcode(操作碼),后面接不等數(shù)目的參數(shù);.NET CLR 有超過(guò) 220個(gè)指令,但是有些指令使用相同的 opcode,所以 opcode 的數(shù)目比指令數(shù)略少。特別注意,.NET 的 opcode 長(zhǎng)度並不固定,大部分的 opcode 長(zhǎng)度是 1 byte,少部分是 2 byte。

下面是一個(gè)簡(jiǎn)單的 C# 原始碼:                

復(fù)制代碼 代碼如下:

using System;
public class Test {
    public 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);
    }
}

將此原始碼編譯之后,可以得到一個(gè) EXE的程序。我們可以通過(guò) ILDASM.EXE(圖-0) 來(lái)反編譯 EXE 以觀察IL。我將 Main() 的 IL 反編譯條列如下,這裡共有十八道IL 指令,有的指令(例如 ldstr 與 box)后面需要接參數(shù),有的指令(例如 ldc.i4.1 與與add)后面不需要接參數(shù)。


圖-0
ldc.i4.1
stloc.0
ldc.i4.2
stloc.1
ldc.i4.3
stloc.2
ldloc.0
ldloc.1
add
ldloc.2
add
stloc.3
ldstr      "i+j+k="
ldloc.3
box        [mscorlib]System.Int32
call       string [mscorlib]System.String::Concat(object, object)
call       void [mscorlib]System.Console::WriteLine(string)
ret

此程式執(zhí)行時(shí),關(guān)鍵的記憶體有三種,分別是:

1、Managed Heap:這是動(dòng)態(tài)配置(Dynamic Allocation)的記憶體,由 Garbage Collector(GC)在執(zhí)行時(shí)自動(dòng)管理,整個(gè)Process 共用一個(gè) Managed Heap。

2、Call Stack:這是由 .NET CLR 在執(zhí)行時(shí)自動(dòng)管理的記憶體,每個(gè) Thread 都有自己專(zhuān)屬的 Call Stack。每呼叫一次 method,就會(huì)使得Call Stack 上多了一個(gè) Record Frame;呼叫完畢之后,此 Record Frame 會(huì)被丟棄。一般來(lái)說(shuō),Record Frame 內(nèi)記錄著 method 參數(shù)(Parameter)、返回位址(Return Address)、以及區(qū)域變數(shù)(Local Variable)。Java VM 和 .NET CLR 都是使用 0, 1, 2… 編號(hào)的方式來(lái)識(shí)別區(qū)別變數(shù)。

3、Evaluation Stack:這是由 .NET CLR 在執(zhí)行時(shí)自動(dòng)管理的記憶體,每個(gè) Thread 都有自己專(zhuān)屬的 Evaluation Stack。前面所謂的堆疊式虛擬機(jī)器,指的就是這個(gè)堆疊。

后面有一連串的示意圖,用來(lái)解說(shuō)在執(zhí)行時(shí)此三種記憶體的變化。首先,在進(jìn)入 Main() 之后,尚未執(zhí)行任何指令之前,記憶體的狀況如圖1 所示:

圖1                

接著要執(zhí)行第一道指令 ldc.i4.1。此指令的意思是:在 Evaluation Stack 置入一個(gè) 4 byte 的常數(shù),其值為 1。執(zhí)行完此道指令之后,記憶體的變化如圖2 所示:

ldc.i4.1:表示加載一個(gè)值為1到堆棧中,該條指令的語(yǔ)法結(jié)構(gòu)是:
ldc.typevalue:ldc指令加載一個(gè)指定類(lèi)型的常量到stack.
ldc.i4.number:ldc指令更加有效.它傳輸一個(gè)整型值-1以及0到8之間的整數(shù)給計(jì)算堆棧

圖2       

接著要執(zhí)行第二道指令 stloc.0。此指令的意思是:從 Evaluation Stack 取出一個(gè)值,放到第 0 號(hào)變數(shù)(V0)中。這裡的第 0 號(hào)變數(shù)其實(shí)就是原始碼中的i。執(zhí)行完此道指令之后,記憶體的變化如圖3 所示:

圖3                

后面的第三道指令和第五道指令雷同於第一道指令,且第四道指令和第六道指令雷同於第二道指令。為了節(jié)省篇幅,我不在此一一贅述。提醒大家第 1 號(hào)變數(shù)(V1)其實(shí)就是原始碼中的 j,且第 2 號(hào)變數(shù)(V2)其實(shí)就是源碼中的 k。圖4~7 分別是執(zhí)行完第三~六道指令之后,記憶體的變化圖:

圖4                

圖5


圖6


圖7

接著要執(zhí)行第七道指令 ldloc.0 以及第八道指令 ldloc.1:分別將 V0(也就是 i)和 V1(也就是 j)的值放到 Evaluation Stack,這是相加前的準(zhǔn)備動(dòng)作。圖8 與圖9 分別是執(zhí)行完第七、第八道指令之后,記憶體的變化圖:

圖8


圖9

接著要執(zhí)行第九道指令 add。此指令的意思是:從 Evaluation Stack 取出兩個(gè)值(也就是 i 和 j),相加之后將結(jié)果放回 Evaluation Stack 中。執(zhí)行完此道指令之后,記憶體的變化如圖10 所示:


圖10

接著要執(zhí)行第十道指令 ldloc.2。此指令的意思是:分別將 V2(也就是 k)的值放到 Evaluation Stack,這是相加前的準(zhǔn)備動(dòng)作。執(zhí)行完此道指令之后,記憶體的變化如圖11 所示:


圖11

接著要執(zhí)行第十一道指令 add。從 Evaluation Stack 取出兩個(gè)值,相加之后將結(jié)果放回 Evaluation Stack 中,此為 i+j+k 的值。執(zhí)行完此道指令之后,記憶體的變化如圖12 所示:


圖12

接著要執(zhí)行第十二道指令 stloc.3。從 Evaluation Stack 取出一個(gè)值,放到第 3 號(hào)變數(shù)(V3)中。這裡的第3號(hào)變數(shù)其實(shí)就是原始碼中的 answer。執(zhí)行完此道指令之后,記憶體的變化如圖13 所示:


圖13

接著要執(zhí)行第十三道指令 ldstr "i+j+k="。此指令的意思是:將 "i+j+k=" 的 Reference 放進(jìn) Evaluation Stack。執(zhí)行完此道指令之后,記憶體的變化如圖14 所示:


圖14

接著要執(zhí)行第十四道指令 ldloc.3。將 V3 的值放進(jìn) Evaluation Stack。執(zhí)行完此道指令之后,記憶體的變化如圖15 所示:


圖15

接著要執(zhí)行第十五道指令 box [mscorlib]System.Int32,從此處可以看出,int到string實(shí)際是進(jìn)行了裝箱操作的,所以會(huì)有性能損失,可以在以后的編碼中減少裝箱操作來(lái)提高性能。此指令的意思是:從 Evaluation Stack 中取出一個(gè)值,將此 Value Type 包裝(box)成為 Reference Type。執(zhí)行完此道指令之后,記憶體的變化如圖16 所示:


圖16

接著要執(zhí)行第十六道指令 call string [mscorlib] System.String::Concat(object, object)。此指令的意思是:從 Evaluation Stack 中取出兩個(gè)值,此二值皆為 Reference Type,下面的值當(dāng)作第一個(gè)參數(shù),上面的值當(dāng)作第二個(gè)參數(shù),呼叫 mscorlib.dll 所提供的 System.String.Concat() method 來(lái)將此二參數(shù)進(jìn)行字串接合(String Concatenation),將接合出來(lái)的新字串放在 Managed Heap,將其 Reference 放進(jìn) Evaluation Stack。值得注意的是:由於 System.String.Concat() 是 static method,所以此處使用的指令是 call,而非 callvirt(呼叫虛擬)。執(zhí)行完此道指令之后,記憶體的變化如圖17 所示:


圖17

請(qǐng)注意:此時(shí) Managed Heap 中的 Int32(6) 以及 String("i+j+k=") 已經(jīng)不再被參考到,所以變成垃圾,等待 GC 的回收。

接著要執(zhí)行第十七道指令 call void [mscorlib] System.Console::WriteLine(string)。此指令的意思是:從 Evaluation Stack 中取出一個(gè)值,此值為 Reference Type,將此值當(dāng)作參數(shù),呼叫 mscorlib.dll 所提供的 System.Console.WriteLine() method 來(lái)將此字串顯示在 Console 視窗上。System.Console.WriteLine() 也是 static method。執(zhí)行完此道指令之后,記憶體的變化如圖18 所示:

圖18

接著要執(zhí)行第十八道指令 ret。此指令的意思是:結(jié)束此次呼叫(也就是 Main 的呼叫)。此時(shí)會(huì)檢查 Evaluation Stack 內(nèi)剩下的資料,由於 Main() 宣告不需要傳出值(void),所以 Evaluation Stack 內(nèi)必須是空的,本范例符合這樣的情況,所以此時(shí)可以順利結(jié)束此次呼叫。而 Main 的呼叫一結(jié)束,程式也隨之結(jié)束。執(zhí)行完此道指令之后(且在程式結(jié)束前),記憶體的變化如圖19 所示:

圖19

通過(guò)此范例,讀者應(yīng)該可以對(duì)于 IL 有最基本的認(rèn)識(shí)。對(duì) IL 感興趣的讀者應(yīng)該自行閱讀 Serge Lidin 所著的《Inside Microsoft .NET IL Assembler》(Microsoft Press 出版)。我認(rèn)為:熟知 IL 每道指令的作用,是 .NET 程式員必備的知識(shí)。.NET 程式員可以不會(huì)用 IL Assembly 寫(xiě)程式,但是至少要看得懂 ILDASM 反編譯出來(lái)的 IL 組合碼。

標(biāo)簽:益陽(yáng) 咸寧 廈門(mén) 攀枝花 南平 拉薩 棗莊 POS機(jī)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《一步步教你讀懂NET中IL(圖文詳解)》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢(xún)

    • 400-1100-266
    垣曲县| 乐清市| 潼南县| 抚松县| 沾化县| 东阳市| 香河县| 自治县| 涟源市| 报价| 得荣县| 北流市| 安康市| 尚义县| 乡宁县| 嘉祥县| 阿勒泰市| 辉南县| 中牟县| 甘泉县| 蒙城县| 城固县| 麟游县| 九龙县| 富宁县| 五莲县| 高邑县| 南华县| 建湖县| 和平区| 中西区| 上犹县| 图木舒克市| 玉屏| 亳州市| 丹凤县| 赤峰市| 郓城县| 宾川县| 琼中| 上杭县|