[你必须知道的.NET]第二十六回:认识元数据和IL(下)
| 說在,開篇之前 |
| 書接上回:? 第二十四回:認(rèn)識元數(shù)據(jù)和IL(上),?第二十五回:認(rèn)識元數(shù)據(jù)和IL(中)? 我們繼續(xù)。? 終于到了,說說元數(shù)據(jù)和IL在JIT編譯時(shí)的角色了,雖然兩個(gè)回合的鋪墊未免鋪張,但是卻絲毫不為過,因?yàn)橹挥谐浞值恼J(rèn)知才有足夠的體會(huì),技術(shù)也是如此。那么,我們就開始沿著方法調(diào)用的軌跡,追隨元數(shù)據(jù)和IL在那個(gè)神秘瞬間所貢獻(xiàn)的力量吧。?? ??????????????????????????????????????????????????????????????????????????????????????www.anytao.com |
?
?
5 元數(shù)據(jù)和IL在JIT編譯時(shí)
CLR最終執(zhí)行的只有本地機(jī)器碼,所以JIT編譯的作用是在運(yùn)行時(shí)將IL代碼解析為機(jī)器碼執(zhí)行。對于JIT編譯,我們會(huì)以專門的篇幅來全面了解,本文只將目光關(guān)注于元數(shù)據(jù)和IL在程序執(zhí)行時(shí)的作用和參與細(xì)節(jié)。首先,IL是基于棧執(zhí)行的,執(zhí)行方法調(diào)用時(shí),方法參數(shù)、局部變量還有返回值等被分配于棧上,并執(zhí)行其調(diào)用過程,既然是關(guān)注JIT編譯時(shí),因此我們自然而然將關(guān)注方法的執(zhí)行,因?yàn)镴IT編譯是以執(zhí)行方法調(diào)用而觸發(fā)的。
首先,對本文開始的代碼加點(diǎn)新料:
// Release : code04, 2009/02/24 // Author : Anytao, http://www.anytao.com // List : Base.cs public class Base {public void M(){Console.WriteLine("M in Base");}public virtual void N(){Console.WriteLine("N in Base");} }還有:
// Release : code05, 2009/02/24 // Author : Anytao, http://www.anytao.com // List : Three.cs public class Three : Base {private static int ID { get; set; }public override void N(){//Something new in ThreeConsole.WriteLine("N in Three");}public void M(){Console.WriteLine("M in Three");M1();}public void M1(){Console.WriteLine("M1 in Three");} }還有執(zhí)行代碼:
static void Main(string[] args) {Base three = new Three();three.M();three.N(); }小窺方法表
以該例而言,執(zhí)行Main方法調(diào)用時(shí),同時(shí)伴隨著對于Three實(shí)例的創(chuàng)建,和相應(yīng)類型信息的加載。我們先將類型信息創(chuàng)建的秘密放在以后的內(nèi)容中,好留點(diǎn)懸念在未來發(fā)揮,哈哈。然而,類型加載一定是在實(shí)例創(chuàng)建之前完成的,也就是我們常常提起的方法表創(chuàng)建。類型加載是由class loader負(fù)責(zé)執(zhí)行的,其過程簡言之就是從元數(shù)據(jù)表中獲取相應(yīng)的類型信息,創(chuàng)建方法表(包含CORINFO_CLASS_STRUCT結(jié)構(gòu)),其結(jié)構(gòu)主要包括非虛方法表和虛方法表,按照繼承的虛方法、新引入的虛方法、實(shí)例方法和靜態(tài)方法的順序排列,以類Three類型為例其CORINFO_CLASS_STRUCT結(jié)構(gòu)可以表示為:
Note: 在本例中Three沒有定義任何靜態(tài)方法,其方法表中父類方法N已有子類覆寫,同時(shí)因?yàn)橛徐o態(tài)成員存在的原因,CLR會(huì)自動(dòng)創(chuàng)建類型構(gòu)造器,詳細(xì)情況可參考《你必須知道的.NET》1.2節(jié) “什么是繼承”。
我們可以同過加載SOS調(diào)試來了解相應(yīng)的方法表信息:
- 在three.N()調(diào)用處打好斷點(diǎn),來查看該時(shí)刻的dump信息,就像一個(gè)內(nèi)存快照,發(fā)現(xiàn)多少東西就看攝影師的水準(zhǔn)。
- 然后,通過dumpheap加載類型信息,獲取方法表地址(0x002a354c),
- 并根據(jù)MT地址,以dumpmt查看相關(guān)的MethodDesc信息,
經(jīng)過簡單的Dump,方法表的信息和我們圖示的信息相差無幾,細(xì)心的觀眾可能會(huì)發(fā)現(xiàn)Dump信息中并不包含Three::cctor(),那么你答對了。圖示的cctor是我基于為Three實(shí)現(xiàn)了類型構(gòu)造器(靜態(tài)構(gòu)造函數(shù))而特別加入的,而代碼中dump的方法表并沒有把類型構(gòu)造器包含在內(nèi),這是個(gè)小粗心,希望細(xì)心的您看得夠透。
執(zhí)行細(xì)則
具體的執(zhí)行過程為為:
- class loader從TypeDef元數(shù)據(jù)表加載相關(guān)元數(shù)據(jù)信息,包括當(dāng)前類型,繼承層次的所有父類和實(shí)現(xiàn)的接口元數(shù)據(jù),根據(jù)這些信息建立CORINFO_CLASS_STRUCT結(jié)構(gòu):
當(dāng)然,對class loader,我們可以進(jìn)行一點(diǎn)知識救急:
?
| 上課啦:class loader |
| Classic Loader是CLR提供的基本組件之一,作用正像其名稱所宣揚(yáng)的那樣,load一個(gè)Class給CLR,class loader將Metadata和IL從PE文件中取出,并加載到運(yùn)行時(shí)內(nèi)存,簡單的說就是我們下面要介紹的全過程縮影。 當(dāng)然,如果你總是對CLR的Classic Loader耿耿于懷,不能釋然。那么,我們也可以參考MSDN的資料來實(shí)現(xiàn)自定義的Classic Loader[How to: Write a Class Loader],希望其中能提供靈光一現(xiàn)的思考。 ??????????????????????????????????????????????????????????????????????????????????????www.anytao.com |
?
- 加載之后,方法執(zhí)行之前的CORINFO_CLASS_STRUCT中所有的方法表槽都保存了方法應(yīng)該執(zhí)行的行為邏輯,這些信息保存在被稱為方法描述(MethodsDesc)的結(jié)構(gòu)中,而MethodDesc則被初始化為指向IL代碼,同時(shí)還包含一個(gè)指向觸發(fā)JIT編譯的PreJitStub地址,如下:
上述所有方法描述都指向各自的IL代碼地址和JIT編譯器,在此我們僅僅以N()方法為例來進(jìn)行說明,詳細(xì)的情況可以參考MSDN相關(guān)內(nèi)容。
- 簡單的說,任何方法第一次執(zhí)行時(shí)都會(huì)首先觸發(fā)執(zhí)行JIT編譯,JIT的主要工作就是將IL代碼翻譯為Native Code,并插入指向Native Code的jmp指令地址覆蓋原來的Call JIT Compiler指令:
- 當(dāng)該方法再次被執(zhí)行時(shí),因?yàn)镸ethodDesc中保存了機(jī)器碼地址,以后的執(zhí)行將不會(huì)執(zhí)行JIT編譯過程而直接執(zhí)行x86(X64)機(jī)器碼,實(shí)現(xiàn)整個(gè)執(zhí)行過程。
縱觀整個(gè)JIT編譯的全過程,其細(xì)節(jié)的實(shí)現(xiàn)遠(yuǎn)比我們這里呈現(xiàn)的復(fù)雜,在粗略的步驟中我們大致了解了元數(shù)據(jù)和IL在整個(gè)過程中的作用、角色和關(guān)系,對了解CLR運(yùn)行機(jī)制而言,適當(dāng)?shù)倪x擇是明智的,如果有更多的心思探索,那么就在以后的歲月中由簡及繁吧,但是相信這一定是一次美妙的旅程。
6 結(jié)論
Metadata描述了靜態(tài)的結(jié)構(gòu),而IL闡釋了動(dòng)態(tài)的執(zhí)行,這一靜一動(dòng)承載了太多的技術(shù)奧秘。
當(dāng)這篇文章行將結(jié)束的時(shí)候,我發(fā)現(xiàn)牽一發(fā)而動(dòng)全身,由此引入的新問題接踵而至,方法調(diào)用、程序集、程序域、CLR加載過程在元數(shù)據(jù)和IL的分析中若隱若現(xiàn),也驅(qū)使我投入注意在后面的《你必須知道的.NET》中,將這些內(nèi)容一一過招。由此才能在復(fù)雜的概念和本質(zhì)之余,由點(diǎn)及面的對所有內(nèi)容綜合把握,形成全面的了解和一條線貫穿的認(rèn)識,那么未來Anytao將要繼續(xù)分享還有:
| 系列預(yù)告 |
??????????????????????????????????????????????????????????????????????????????????????www.anytao.com |
限于繁忙的原因,我無法給出一個(gè)清晰的時(shí)間表,但力圖每次的內(nèi)容都給您出足夠的收獲,如果你對.NET始終心懷興致,那么敬請期待《你必須知道的.NET》更多精彩。?
支持anytao的創(chuàng)業(yè)產(chǎn)品Worktile
Worktile,新一代簡單好用、體驗(yàn)極致的團(tuán)隊(duì)協(xié)同、項(xiàng)目管理工具,讓你和你的團(tuán)隊(duì)隨時(shí)隨地一起工作。完全免費(fèi),現(xiàn)在就去了解一下吧。
https://worktile.com
- 《你必須知道的.NET》第3章 “一切從IL開始”
- DonBox,《.NET本質(zhì)論》
- http://www.sloppycode.net/articles/inside-net-assemblies-and-metadata.aspx
- http://www.codeproject.com/KB/dotnet/dotnetformat.aspx
溫故知新
總結(jié)
以上是生活随笔為你收集整理的[你必须知道的.NET]第二十六回:认识元数据和IL(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 父亲节不知道送啥?血压、心电都能测的华为
- 下一篇: asp.net ajax控件工具集 Au