成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳解CLR中Jit編譯發(fā)生的過程

開發(fā) 后端
這里將介紹CLR中Jit編譯發(fā)生的過程,通過筆者深入的探討,希望能對大家了解Jit的發(fā)生過程是怎么樣的有所了解。
理解在CLR中Jit編譯發(fā)生的過程,了解這個首先要從CLR談起。CLR(公共語言運行時,Common Language Runtime)和Java虛擬機一樣也是一個運行時環(huán)境,它負責資源管理(內存分配和垃圾收集),并保證應用和底層操作系統(tǒng)之間必要的分離。

CLR是如何找到托管代碼的入口方法并對其Jit的呢?Jit編譯的發(fā)生過程是怎么樣的呢?Jit編譯器和Metadata表又有什么關系呢?本文試圖尋找出答案,在此之前,不妨先了解一下CLR Header的大致結構。

以如下代碼為例:

  1. Example  
  2. using System;  
  3.  
  4. namespace CLRTesing  
  5. {  
  6. class Program  
  7. {  
  8. static void Main(string[] args)  
  9. {  
  10. System.Console.WriteLine("Hello World!");  
  11. Console.ReadKey();  
  12.  
  13. new P().Display();  
  14.  
  15.  
  16. }  
  17.  
  18. Program()  
  19. {  
  20. Console.WriteLine("Constructor.");  
  21. Console.ReadKey();  
  22. }  
  23.  
  24. static Program()  
  25. {  
  26. Console.WriteLine("Static constructor.");  
  27. Console.ReadKey();  
  28. }  
  29. }  
  30.  
  31. class P  
  32. {  
  33. public void Display()  
  34. {  
  35. System.Console.WriteLine("P!");  
  36. Console.ReadKey();  
  37.  
  38. new Q().Display();  
  39. Console.ReadKey();  
  40. }  
  41. }  
  42.  
  43. class Q  
  44. {  
  45. public void Display()  
  46. {  
  47. System.Console.WriteLine("Q!");  
  48. Console.ReadKey();  
  49. }  
  50. }  

編譯后通過dumpbin工具的到其CLR Header,如圖所示:

dumpbin工具

從圖中可以看到,CLR Header由以下幾個部分組成:

1、CB:表示CLR Header的大小,單位是byte;

2、Run time version:運行時版本,包含兩部分MajorRuntimeVersion和MinorRuntimeVersion;

3、Metadata Directory:指出Metadata table的RVA和其大小;

4、Flag:這個標識主要是供加載器使用,flag值為0x00000001表示當前runtime image僅由IL代碼組成并且對CPU沒有特殊要求;值為0x00000002表示image只能被加載到32位機中,值為0x00010000表示運行時和jit編譯器需要追蹤方法的調試信息;

5、EntryPointToken:Metadata 表中標記為EntryPoint的方法的MethodDef;

6、Resources Directory:CLR的資源,也就是托管資源的RVA和大小,注意與PE文件中存儲Win32資源的section不同;

7、StrongNameSignature Directory:PE文件中供CLR加載器使用的哈希值所處RVA和大小;

8、CodeManagerTable Directory:Code Manager 表的RVA和其大小;

9、VTableFixups Directory:由非托管C++類型中虛方法的指針組成的數組;

10、ExportAddressTableJumps Directory:跳轉地址表的RVA和大小;

11、ManagedNativeHeader Directory:一般情況下為0。

以上結構可以從CorHdr.h文件中看出,如果裝的是Visual Studio 2005,這個文件在\Microsoft Visual Studio 8\SDK\v2.0\include\。

查看托管PE文件的工具有很多,不用很復雜的,就園子里的大牛Anders Liu寫的CliPeViwer就很好用,用Reflector可以偷窺其代碼哦。

那么在上面這個結構中我最關心的是Metadata directory和EntryPointToken,Metadata directory存提供了原數據所在內存地址的范圍,EntryPointToken告訴我們在原數據表中哪個token標識的方法是入口方法,這里一定是方法,所以這個token是以6開頭的一個數。

回到主題,我們從CLR已經被載入內存、mscorwks.dll中的_CorExeMain2方法接管主線程開始說起:

1、_CorExeMain2方法會調用System Domain中的SystemDomain::ExecuteMainMethod方法,然后由此方法再去調用其它方法(具體什么方法參見深入了解CLR的加載過程一文中的第8步), 通過MetaData表提供的接口查找包含.entrypoint的類型,接著返回入口方法(在C#中這個入口方法一定是Main方法)的一個MethodDesc類型的實例;獲取MethodDesc類型實例的這個過程我認為是:CLR通過讀取MetaData表,定位入口方法所屬的類型,將包含該類型的Module載入,然后建立這個類型的EECLASS(EECLASS結構中包含重要信息有:指向當前類型父類的指針、指向方法表的指針、實例字段和靜態(tài)字段等)和這個類型所包含方法的Method Table(方法表由一個個Method Descripter組成,具體到內存中就是指向若干MethodDesc類型實例的地址),通過EEClass::FindMethod方法找到并返回入口方法的MethodDesc類型實例。

MethodDesc這個類型很有意思,它有兩個重要的部分,一個部分叫做m_CodeOrIL,用來存儲編譯好的MSIL在內存中的地址,初值為ffffffffffffffff,另一個部分叫做Stub,如果當前代碼沒有被編譯為本地CPU指令,那么通過這個Stub會觸發(fā)對Jit編譯器的調用。

執(zhí)行上述代碼,

[[5737]]

用Windbg 查看,如下:

  1. Windbg1  
  2. 0:000> !name2ee *!CLRTesing.Program  
  3. Module: 790c2000 (mscorlib.dll)  
  4. --------------------------------------  
  5. Module: 00a72c3c (Hello.exe)  
  6. Token: 0x02000002  
  7. MethodTable: 00a73048  
  8. EEClass: 00a7129c  
  9. Name: CLRTesing.Program  
  10.  
  11. 0:000> !name2ee *!CLRTesing.P  
  12. Module: 790c2000 (mscorlib.dll)  
  13. --------------------------------------  
  14. Module: 00a72c3c (Hello.exe)  
  15. Token: 0x02000003  
  16. MethodTable:   
  17. EEClass:   
  18. Name: CLRTesing.P  
  19.  
  20. 0:000> !dumpmt -md 00a73048  
  21. EEClass: 00a7129c  
  22. Module: 00a72c3c  
  23. Name: CLRTesing.Program  
  24. mdToken: 02000002(D:\test\Hello\bin\Debug\Hello.exe)  
  25. BaseSize: 0xc  
  26. ComponentSize: 0x0  
  27. Number of IFaces in IFaceMap: 0  
  28. Slots in VTable: 7  
  29. --------------------------------------  
  30. MethodDesc Table  
  31.  Entry MethodDescJIT Name  
  32. 79371278 7914b928 PreJIT System.Object.ToString()  
  33. 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)  
  34. 7936b3d0 7914b948 PreJIT System.Object.GetHashCode()  
  35. 793624d0 7914b950 PreJIT System.Object.Finalize()  
  36. 00a7c011 00a73030 NONE CLRTesing.Program.Main(System.String[])  
  37. 00a7c015 00a73038 NONE CLRTesing.Program..ctor()  
  38. 00da0070 00a73040JIT CLRTesing.Program..cctor() 

CLRTesing.Program類型的靜態(tài)構造函數執(zhí)行時,入口方法Main和CLRTesing.Program的實例構造函數還沒有被Jit,Main方法中引用到的CLRTesing.P類型也沒有被加載,所以它的Method Table和EEClass結構也沒有建立起來。

#p#

2、在Windbg中detach debuggee,隨便敲一個字符讓程序繼續(xù)運行;接著,入口方法Main開始執(zhí)行,

入口方法Main開始執(zhí)行

因為Main方法第一次執(zhí)行,所以通過Stub,Jit編譯器會被喚起,由于Main方法引用了CLRTesing.P類型,那么在執(zhí)行前會將CLRTesing.P類型載入,并建立Method Table和其EEClass結構,當然這個建立過程也要去查找MetaData表,我認為這個過程是這樣的:

Main方法被調用,由于它沒有被Jit過,CLR會通過Main方法的MethodDesc結構的Stub對Jit編譯器進行調用,CLR通過MetaData表的接口找到Main方法對應的Token,如下:

Jit編譯

我們可以看到Main方法的RVA是0x00002050,于是去PE文件的.Text section中的Raw Data中查找image base+RVA這個位置處的IL代碼,接著Jit編譯器會對這段IL代碼進行驗證,驗證過程通過調用CheckIL方法來實現,這個方法的簽名可以是這樣的:

  1. CHECK CheckIL(RVA il);  
  2. CHECK CheckIL(RVA il, COUNT_T size); 

驗證結束后把這段IL代碼編譯成本地CPU指令,將編譯后后的CPU指令存到內存并修改Main方法的MethodDesc結構中m_CodeOrIL和Stub的值,讓它們指向這個新的內存地址,當這個方法被再次調用的時候就會直接通過這個地址訪問到本地CPU指令而不會觸發(fā)第二次編譯。對于這個過程大家的看法呢?用Windbg查看各對象情況:

  1. Windbg2  
  2. 0:000> !name2ee *!CLRTesing.Program  
  3. Module: 790c2000 (mscorlib.dll)  
  4. --------------------------------------  
  5. Module: 00a72c3c (Hello.exe)  
  6. Token: 0x02000002  
  7. MethodTable: 00a73048  
  8. EEClass: 00a7129c  
  9. Name: CLRTesing.Program  
  10.  
  11. 0:000> !name2ee *!CLRTesing.P  
  12. Module: 790c2000 (mscorlib.dll)  
  13. --------------------------------------  
  14. Module: 00a72c3c (Hello.exe)  
  15. Token: 0x02000003  
  16. MethodTable: 00a730b8  
  17. EEClass: 00a71730  
  18. Name: CLRTesing.P  
  19.  
  20. 0:000> !name2ee *!CLRTesing.Q  
  21. Module: 790c2000 (mscorlib.dll)  
  22. --------------------------------------  
  23. Module: 00a72c3c (Hello.exe)  
  24. Token: 0x02000004  
  25. MethodTable:   
  26. EEClass:   
  27. Name: CLRTesing.Q  
  28.  
  29. 0:000> !dumpmt -md 00a73048  
  30. EEClass: 00a7129c  
  31. Module: 00a72c3c  
  32. Name: CLRTesing.Program  
  33. mdToken: 02000002(D:\test\Hello\bin\Debug\Hello.exe)  
  34. BaseSize: 0xc  
  35. ComponentSize: 0x0  
  36. Number of IFaces in IFaceMap: 0  
  37. Slots in VTable: 7  
  38. --------------------------------------  
  39. MethodDesc Table  
  40.  Entry MethodDescJIT Name  
  41. 79371278 7914b928 PreJIT System.Object.ToString()  
  42. 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)  
  43. 7936b3d0 7914b948 PreJIT System.Object.GetHashCode()  
  44. 793624d0 7914b950 PreJIT System.Object.Finalize()  
  45. 00da00b0 00a73030JIT CLRTesing.Program.Main(System.String[])  
  46. 00a7c015 00a73038 NONE CLRTesing.Program..ctor()  
  47. 00da0070 00a73040JIT CLRTesing.Program..cctor()  
  48.  
  49. 0:000> !dumpmt -md 00a730b8  
  50. EEClass: 00a71730  
  51. Module: 00a72c3c  
  52. Name: CLRTesing.P  
  53. mdToken: 02000003(D:\test\Hello\bin\Debug\Hello.exe)  
  54. BaseSize: 0xc  
  55. ComponentSize: 0x0  
  56. Number of IFaces in IFaceMap: 0  
  57. Slots in VTable: 6  
  58. --------------------------------------  
  59. MethodDesc Table  
  60.  Entry MethodDescJIT Name  
  61. 79371278 7914b928 PreJIT System.Object.ToString()  
  62. 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)  
  63. 7936b3d0 7914b948 PreJIT System.Object.GetHashCode()  
  64. 793624d0 7914b950 PreJIT System.Object.Finalize()  
  65. 00a7c04c 00a730a8 NONE CLRTesing.P.Display()  
  66. 00a7c058 00a730b0 NONE CLRTesing.P..ctor() 

我們可以發(fā)現Main方法已經被Jit,且它引用的CLRTesing.P類型的相關結構也已經建立起來了,而CLRTesing.P類型的Display方法所引用的CLRTesing.Q類型沒有被載入。

總結一下,Jit編譯針對的對象總是方法,不論是入口方法還是其他方法的Jit過程都類似上述過程,Metadata這這里的作用不言而喻,可以說沒有Metadata的支持就無法進行Jit,我覺得Meatadata在Jit編譯期間的作用至少有三個:

1、Jit編譯器通過查找Metadata來找到入口方法;

2、Jit編譯器通過查找Metadata來定位待編譯方法并利用其RVA找到存儲于PE文件中的IL代碼在內存中的實際地址;

3、Jit編譯器在找到IL代碼并準備編譯為本地CPU指令前所進行的IL代碼驗證同樣會用到Metadata,例如,驗證方法的合法性需要去核實方法參數數量是正確的、傳給方法的每個參數是否都有正確的類型、方法返回值是否正確等等。

文中是一些我通過Shared Source Common Language Infrastructure(SSCLI)看到的和感覺到的東西,希望能給大家理解Jit提供一點幫助,如果有錯誤的地方也請大家指出,大家一起學習。

最后要說明的是,SSCLI里東西僅作為理解CLR使用,與MS真正實現CLR的過程可能不一樣。最后,大家在看SSCLI的時候可以使用Source Insight,個人感覺還挺好用。

 SSCLI的下載地址是:http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en。

本文來自Leo Zhang博客園文章《深入了解Jit編譯發(fā)生的過程

【編輯推薦】

  1. C#treeview遞歸操作數據庫淺析
  2. C#遞歸樹實現實例簡析
  3. C#打開記事本實現實例解析
  4. C#調用記事本實例淺析
  5. C#日期格式化方法簡析
責任編輯:彭凡 來源: 博客園
相關推薦

2009-10-23 09:36:25

.Net Compac

2009-08-24 11:36:27

CLR加載過程

2023-10-31 11:46:32

編譯器托管CLR

2022-05-18 07:58:21

Linux程序編譯代碼

2020-11-09 14:41:58

iOS 14.2蘋果JIT

2009-09-18 10:40:05

CLR存儲過程

2024-11-27 16:25:54

JVMJIT編譯機制

2009-10-22 13:02:47

SQL CLR存儲過程

2009-09-18 14:09:57

SQL CLR存儲過程

2009-10-22 14:05:55

CLR存儲過程

2009-10-22 18:06:31

CLR存儲過程

2009-09-17 19:19:17

CLR存儲過程

2011-08-17 17:29:32

Windows編譯MySQL

2009-10-19 14:25:16

靜態(tài)構造函數

2009-09-18 10:55:17

CLR存儲過程

2009-03-11 10:29:23

代碼契約.NETCLR

2011-05-03 10:31:59

噴墨打印機注墨誤區(qū)

2009-10-22 15:09:40

CLR存儲過程

2009-05-15 09:33:52

開發(fā)線程沖突lock

2022-04-10 10:57:06

eBPFJIT即時編譯
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美成人一区二区三区片免费 | 国产成人久久精品一区二区三区 | 欧美美女被c | 欧美一区二区三区电影 | 欧美精品成人一区二区三区四区 | 欧美久久视频 | 亚洲国产精久久久久久久 | 国产蜜臀| 黄网站免费入口 | 亚洲久草 | 一级免费毛片 | 成人国产精品视频 | 一区二区三区四区av | 国产一区在线免费观看视频 | 国产农村一级片 | 黄色网络在线观看 | аⅴ资源新版在线天堂 | 欧美极品在线观看 | 成人免费小视频 | 爱操av| 最近最新中文字幕 | 成人av资源在线 | 欧美国产日韩在线 | 亚洲精品区 | 夜夜摸夜夜操 | 一本岛道一二三不卡区 | 日韩精品视频中文字幕 | 成人深夜小视频 | 欧美午夜激情在线 | 亚洲精品高清视频在线观看 | 美女爽到呻吟久久久久 | 国产一区二区在线免费观看 | 日韩一区二区在线视频 | 99视频免费播放 | 欧美激情精品久久久久久变态 | 夜夜爽夜夜操 | 99福利视频| 一区二区成人 | 免费黄色片视频 | 国产日产欧产精品精品推荐蛮挑 | 91久久国产综合久久 |