1. 程式人生 > >一步步教你讀懂NET中IL(附帶圖)

一步步教你讀懂NET中IL(附帶圖)

    一步步教你讀懂NET中IL(附詳細圖)

  接觸NET也有1年左右的時間了,NET的內部如何實現對我產生了很大的吸引力,在msdn上找到一篇關於NET的IL程式碼的圖解說明,寫的挺不錯的,在此基礎上加上個人的理解,每一個步驟都附帶圖解說明,如果你以前對NET中IL感覺晦澀難懂,頭昏腦漲的時候,沒關係,我相信這篇文章能讓你們對IL有一個比較詳細的理解,如果還不能幫助您,我也只能表示抱歉,因為這篇文章算比較詳細的了。個人覺得:能對這些底部的程式碼是如何實現的進行了解和熟練的話,對以後自己寫程式碼是有很大幫助的,好了,廢話不多說,現整理如下:

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

      本文章以一個實際的例子,讓你瞭解堆疊式 VM 的運作原理,並對 .NET IL(Intermediate Language)有最基本的領略。

      下面是一個簡單的 C# 原始碼:                 

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);
    }
}

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

  圖-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

此程式執行時,關鍵的記憶體有三種,分別是:

1、Managed Heap:這是動態配置(Dynamic Allocation)的記憶體,由 Garbage Collector(GC)在執行時自動管理,整個Process 共用一個 Managed Heap。

2、Call Stack:這是由 .NET CLR 在執行時自動管理的記憶體,每個 Thread 都有自己專屬的 Call Stack。每呼叫一次 method,就會使得Call Stack 上多了一個 Record Frame;呼叫完畢之後,此 Record Frame 會被丟棄。一般來說,Record Frame 內記錄著 method 引數(Parameter)、返回位址(Return Address)、以及區域變數(Local Variable)。Java VM 和 .NET CLR 都是使用 0, 1, 2… 編號的方式來識別區別變數。

3、Evaluation Stack:這是由 .NET CLR 在執行時自動管理的記憶體,每個 Thread 都有自己專屬的 Evaluation Stack。前面所謂的堆疊式虛擬機器器,指的就是這個堆疊。

後面有一連串的示意圖,用來解說在執行時此三種記憶體的變化。首先,在進入 Main() 之後,尚未執行任何指令之前,記憶體的狀況如圖1 所示:

對 1

圖1                

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

ldc.i4.1:表示載入一個值為1到堆疊中,該條指令的語法結構是:

ldc.typevalue:ldc指令載一個指定型別的常量到stack.
ldc.i4.number:ldc指令更加有效.它傳輸一個整型值-1以及0到8之間的整數給計算堆疊

對 2

圖2                

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

對 3

圖3                

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

對 4

圖4                

對 5

圖5

對 6圖6

對 7圖7

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

對 8

圖8

對 9圖9

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

對 10圖10

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

對 11圖11

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

對 12圖12

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

對 13圖13

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

對 14圖14

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

對 15圖15

接著要執行第十五道指令 box [mscorlib]System.Int32,從此處可以看出:值型別與字串進行+操作的時候,由於呼叫的是內部的String.Concat方法,而這個方法是傳遞(object,object)這2個引數,所以會進行裝箱操作(box),因此會有效能上的損失,可以在以後的程式碼編寫中避免不必要的裝箱操作。此指令的意思是:從 Evaluation Stack 中取出一個值,將此 Value Type 包裝(box)成為 Reference Type。執行完此道指令之後,記憶體的變化如圖16 所示:

對 16圖16

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

對 17圖17

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

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

對 18圖18

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

對 19圖19

通過此範例,讀者應該可以對於 IL 有最基本的認識。對 IL 感興趣的讀者應該自行閱讀 Serge Lidin 所著的《Inside Microsoft .NET IL Assembler》(Microsoft Press 出版)。我認為:熟知 IL 每道指令的作用,是 .NET 程式員必備的知識。.NET 程式員可以不會用 IL Assembly 寫程式,但是至少要看得懂 ILDASM 反編譯出來的 IL 組合碼。

內容部分引用自msdn

相關推薦

步步NETIL附帶

    一步步教你讀懂NET中IL(附詳細圖)   接觸NET也有1年左右的時間了,NET的內部如何實現對我產生了很大的吸引力,在msdn上找到一篇關於NET的IL程式碼的圖解說明,寫的挺不錯的,在此基礎上加上個人的理解,每一個步驟都附帶圖解說明,如果你以前對NET中IL感覺晦澀難懂,頭昏腦漲的時候,沒關

步步NETIL(圖文詳解)

接觸NET也有1年左右的時間了,NET的內部實現對我產生了很大的吸引力。個人覺得:能對這些底部的實現進行了解和熟練的話,對以後自己寫程式碼是有很大幫助的,好了,廢話不多說,請看下邊: .NET CLR 和 Java VM 都是堆疊式虛擬機器器(Stack-Based VM),

大神步步ORB演算法,贊!!

工作就沒有在學校時間上有那麼自由了,最近出差了快一個月,部落格也就落下了。現在開始一點點的來學習orb-slam2,將自己的學習過程寫出,望大家指正批評。 至於為什麼學習orb-slam2,主要這比較完整的實現了slam的整個過程,論文發表在IEEE Transaction

步步建立自己的數字貨幣代幣進行ICO

本文從技術角度詳細介紹如何基於以太坊ERC20建立代幣的流程. 寫在前面 本文所講的代幣是使用以太坊智慧合約建立,閱讀本文前,你應該對以太坊、智慧合約有所瞭解,如果你還不瞭解,建議你先看以太坊是什麼 代幣Token 如果不那麼追求精確的定義,代幣就是數字貨幣,比特

步步大資料時代下的“使用者畫像”

  什麼是使用者畫像      互動設計之父Alan Cooper最早提出了使用者畫像(persona)的概念,認為“使用者畫像是真實使用者的虛擬代表,是建立在一系列真實資料之上的目標使用者模型”。通過對客戶多方面的資訊的瞭解,將多種資訊集合在一起並形成在一定型別上的獨特的特徵與氣質,這就形成了使用者

【Android】從無到有:手把手步步使用最簡單的Fragment

轉載請註明出處,原文連結:https://blog.csdn.net/u013642500/article/details/80585416 【本文適用讀者】         用程式碼建立並使用了 Fragment,新增 Fragment 之

【Android】從無到有:手把手步步使用最簡單的Fragment

轉載請註明出處,原文連結:https://blog.csdn.net/u013642500/article/details/80579389 【本文適用讀者】         targetSdkVersion 版本大於等於 21,即 app 即將有可能

【Android】從無到有:手把手步步使用最簡單的 Fragment

轉載請註明出處,原文連結:https://blog.csdn.net/u013642500/article/details/80515227 【本文適用讀者】         知道 Fragment 是什麼,不知

寫一個快遞查詢APP適合新手

前言: 水平:自學Android十五天,以前有過混日子的程式設計經驗。 目標: 《第一行程式碼》學完之後,總想寫個APP,天氣的APP寫了個初版,後面再說,今天演示的是製作快遞查詢APP的整個經過。 適合人群:新手 工具:A

步步如何在Ubuntu虛擬機器安裝QEMU並模擬模擬arm 開發環境uImage u-boot

初次接觸qemu是因為工作的需要,有時候下了班,可能需要在家研究一些東西,因為博主用到arm環境,這時候博主比較小氣,不願花錢買開發板,當然博主在這裡給大家的建議是,如果要真正學懂arm構架的相關知識,還是單獨買一塊arm的開發板,慢慢摸索吧,畢竟這才是最好的學習方法,如果

機器學習:步步理解反向傳播方法

神經網絡 方法 數學 https 以及 看到了 兩個 簡單的 down http://www.360doc.com/content/17/0209/17/10724725_627833018.shtml 數學完全看不懂 看到了這篇通過示例給出反向傳播的博文A Step by

步步創建自己的數字貨幣代幣進行ICO

允許 總量 ted exe init allow transfer ner 定義 本文從技術角度詳細介紹如何基於以太坊ERC20創建代幣的流程. 寫在前面 本文所講的代幣是使用以太坊智能合約創建,閱讀本文前,你應該對以太坊、智能合約有所了解,如果你還不了解,建議你先看以太坊

步步開發、部署第一個去中心化應用(Dapp) - 寵物商店

區塊鏈今天我們來編寫一個完整的去中心化(區塊鏈)應用(Dapps), 本文可以和編寫智能合約結合起來看。 寫在前面 閱讀本文前,你應該對以太坊、智能合約有所了解,如果你還不了解,建議你先看以太坊是什麽除此之外,你最好還了解一些HTML及JavaScript知識。 本文通過實例教大家來開發去中心化應用,應用效果

科普貼 | 以太坊代幣錢包MyEtherWallet使用教程,步步玩轉MEW

按鈕 wid isp 查詢 到你 pan fail VC oam MyEtherWallet 是一個以太坊的網頁錢包,使用非常簡單,打開網頁就可以使用,源代碼開源,不會在服務器上存儲用戶的錢包信息如私鑰和密碼。支持 Ledger Wallet、TREZOR 等硬件錢包

我的設計模式系列----工廠玩法

info spa trac 不變 等等 動態生成 接受 說過 類型 一、時常聽到各種工廠,解讀工廠 很多人大概從開始工作起,逐漸接觸聽說過各項工廠類名詞,什麽“簡單工廠模式”啊,什麽“靜態工廠方法”啊,什麽&l

步步如何打造一個網站克隆工具仿站

obj cell ins 地址 line load mail als () 前兩天朋友叫我模仿一個網站,剛剛開始,我一個頁面一個頁面查看源碼並復制和保存,花了我很多時間,一個字“累”,為了減輕工作量,我寫了個網站“克隆工具”,一鍵克隆,比起人工操作, 效率提高了200%以上

【Android】從無到有:手把手步步構建並使用RecyclerView

轉載請註明出處,原文連結:https://blog.csdn.net/u013642500/article/details/80480906 【AS版本】 【新增依賴】 1、開啟 Project Structural。(可點選圖示 ,也可以在File選單中開啟,也可以按 Ct

20181117——步步開發、部署第一個去中心化應用(Dapp) - 寵物商店

DApp是Decentralized Application 分散式應用 npm Node Package Manager. 包管理工具 用來下載安裝升級解除安裝安裝包 完了沒發出來,結果快取都沒了。 用MetaMask測試私有網路 從Ganache建立的賬戶中選擇一個匯入

slam是什麼意思?文帶SLAM

  SLAM是Simultaneous localization and mapping縮寫,意為“同步定位與建圖”,主要用於解決機器人在未知環境運動時的定位與地圖構建問題,為了讓大家更多的瞭解SLAM,以下將從SLAM的應用領域、SLAM框架、SLAM分類(基於感測器的SLAM分類)

步步使用rem適配不同螢幕的移動裝置

本文轉載自:https://www.cnblogs.com/dannyxie/p/6640903.html 感謝分享 1.先說說幾個前端常用的幾個單位的概論: 1、px (pixel,畫素):是一個虛擬長度單位,是計算機系統的數字化影象長度單位,如果px要換算成物理長度,需要指定精度