深入理解.NET 的JIT編譯方式
2024-07-10 12:58:40
供稿:網友
 
本文來源于網頁設計愛好者web開發社區http://www.html.org.cn收集整理,歡迎訪問。clr只執行本機的機器代碼。有兩種方式產生本機的機器代碼:實時編譯(jit)和預編譯方式(產生native image)。下面,我想談談jit。
clr使用類型的方法表來路由所有的方法調用。類型的方法表由多個入口項組成。每個入口項指向一個唯一的存根例程(stub routine)。初始化時,每個存根例程包含一個對于clr的jit編譯器的調用(它由內部的prestubworker程序公開)。在jit編譯器生成本機代碼后,它會重寫存根例程,插入一個jmp指令跳轉到剛才jit編譯器的代碼。只有當要調用某個方法時,jit編譯器才會將cil的方法體編譯為相應的本機機器碼版本。這樣可以優化程序的工作集。
對于如下的例子:
//using system;
public class bob{
static int x;
static void a(){x+=2;}
static void b(){x+=3;}
static void c(){x+=4;}
public static void f()
{
 c();
 b();
 a();
}
}
public class myclass
{
 public static void main()
 {
 
 bob.f();
 }
 }
用調試器進行jit調試。
首先,看一下每個方法的匯編顯示:
main()的匯編顯示為:
push ebp
mov ebp,esp
//調用bob.f()方法
call dword ptr ds:[00975394h]
nop 
pop ebp
ret
[注]00975394h是bob.f()在corinfo_class_struct該內部數據結構中對應的內存地址,該地址中的內容是對應的存根例程的開始地址。
f()的匯編顯示為:
push ebp
mov ebp,esp
//調用bob.c()方法
call dword ptr ds:[00975390h] 
//調用bob.b()方法 
call dword ptr ds:[0097538ch]
//調用bob.a()方法
call dword ptr ds:[00975388h]
nop
pop ebp
ret
[注]00975390、0097538c、00975388分別為bob.c()、bob.b()、bob.a()在corinfo_class_struct該內部數據結構中對應的內存地址,該地址中的內容是對應的存根例程的開始地址。
c()的匯編顯示為:
push ebp
mov ebp,esp
add dword ptr ds:[0097539ch],4
nop
pop ebp
ret
[注]0097539c是bob.x的內存地址。
b()的匯編顯示為:
push ebp
mov ebp,esp
add dword ptr ds:[0097539ch],3
nop
pop ebp
ret
[注]0097539c是bob.x的內存地址。
a()的匯編顯示為:
push ebp
mov ebp,esp
add dword ptr ds:[0097539ch],2
nop
pop ebp
ret
[注]0097539c是bob.x的內存地址。
下面,讓我們看看調試時,地址為00975394h、00975390h、0097538ch、00975388h中的內容:
0x00975384 2b 85 bf 79 03 53 97 00 13 53 97 00 23 53 97 00 
0x00975394 33 53 97 00 43 53 97 00 00 00 00 00 00 00 00 00
綠色的是bob.f()在corinfo_class_struct該內部數據結構中對應的內存地址;
紫色的是bob.c()在corinfo_class_struct該內部數據結構中對應的內存地址;
灰色的是bob.b()在corinfo_class_struct該內部數據結構中對應的內存地址;
黃色的是bob.a()在corinfo_class_struct該內部數據結構中對應的內存地址;
[注]紅色的內容則是bob.x的值哦!不信嗎?那你看看下面的調試過程中紅色處的值的變化,就明白了:
進入f()前為:
0x00975384 2b 85 bf 79 03 53 97 00 13 53 97 00 23 53 97 00 
0x00975394 33 53 97 00 43 53 97 00 00 00 00 00 00 00 00 00
c()加4后變為:
0x00975384 2b 85 bf 79 03 53 97 00 13 53 97 00 23 53 97 00 
0x00975394 33 53 97 00 43 53 97 00 04 00 00 00 00 00 00 00 
加3后變為:
0x00975384 2b 85 bf 79 03 53 97 00 13 53 97 00 23 53 97 00 
0x00975394 33 53 97 00 43 53 97 00 07 00 00 00 00 00 00 00
加2后變為:
0x00975384 2b 85 bf 79 03 53 97 00 13 53 97 00 23 53 97 00 
0x00975394 33 53 97 00 43 53 97 00 09 00 00 00 00 00 00 00
下面讓我們看看在調用bob.f()之前存根例程處的內容:
0x00975303 e8 d0 52 7d ff 04 00 10 00 50 20 00 c0 02 00 fe 
0x00975313 e8 c0 52 7d ff 05 00 10 00 6c 20 00 c0 03 00 fc 
0x00975323 e8 b0 52 7d ff 06 00 10 00 88 20 00 c0 04 00 fa 
0x00975333 e8 a0 52 7d ff 07 00 10 00 a4 20 00 c0 05 00 f8 
綠色處是bob.f()的存根例程的內容;
紫色處是bob.c()的存根例程的內容;
灰色處是bob.b()的存根例程的內容;
黃色處是bob.a()的存根例程的內容;
下面讓我們看看在進入bob.f()方法體之后存根例程處的內容:
0x00975303 e8 d0 52 7d ff 04 00 10 00 50 20 00 c0 02 00 fe 
0x00975313 e8 c0 52 7d ff 05 00 10 00 6c 20 00 c0 03 00 fc 
0x00975323 e8 b0 52 7d ff 06 00 10 00 88 20 00 c0 04 00 fa 
0x00975333 e9 40 ad 39 06 07 00 10 00 78 00 d1 06 05 00 f8 
易見,只有bob.f()的存根例程的內容起了變化。這說明,jit編譯器被調用了。同時,編譯器將f()的cil方法體轉換為地址空間中的機器碼版本。再替換了存根例程的原內容。替換后,f()方法的機器碼版本的首地址是0x06d10078(藍色標注處的內容)。你不信嗎?那好,我們看一下0x06d10078處的內存內容:
0x06d10078 55 8b ec ff 15 90 53 97 00 ff 15 8c 53 97 00 ff 
0x06d10088 15 88 53 97 00 90 5d c3 00 00 00 00 00 00 00 00 
你不妨再回頭看看f()的匯編顯示,這里:
紫色處不正是對bob.c()在corinfo_class_struct該內部數據結構中對應的內存地址;
灰色處不正是對bob.b()在corinfo_class_struct該內部數據結構中對應的內存地址;
黃色處不正是對bob.a()在corinfo_class_struct該內部數據結構中對應的內存地址;
不正好對應了f()匯編顯示中:
//調用bob.c()方法
call dword ptr ds:[00975390h] 
//調用bob.b()方法 
call dword ptr ds:[0097538ch]
//調用bob.a()方法
call dword ptr ds:[00975388h]
明白了吧!好了,下面對于c() 、b()、a()的調用也是一樣的道理。對于接下來的調試會有如下的內存顯示:
接下來的存根例程的內容:
進入c()后變為:
0x00975303 e8 d0 52 7d ff 04 00 10 00 50 20 00 c0 02 00 fe 
0x00975313 e8 c0 52 7d ff 05 00 10 00 6c 20 00 c0 03 00 fc 
0x00975323 e9 78 ad 39 06 06 00 10 00 a0 00 d1 06 04 00 fa 
0x00975333 e9 40 ad 39 06 07 00 10 00 78 00 d1 06 05 00 f8 
進入b()后變為:
0x00975303 e8 d0 52 7d ff 04 00 10 00 50 20 00 c0 02 00 fe 
0x00975313 e9 a8 ad 39 06 05 00 10 00 c0 00 d1 06 03 00 fc 
0x00975323 e9 78 ad 39 06 06 00 10 00 a0 00 d1 06 04 00 fa 
0x00975333 e9 40 ad 39 06 07 00 10 00 78 00 d1 06 05 00 f8 
進入a()后變為:
0x00975303 e9 d8 ad 39 06 04 00 10 00 e0 00 d1 06 02 00 fe 
0x00975313 e9 a8 ad 39 06 05 00 10 00 c0 00 d1 06 03 00 fc 
0x00975323 e9 78 ad 39 06 06 00 10 00 a0 00 d1 06 04 00 fa 
0x00975333 e9 40 ad 39 06 07 00 10 00 78 00 d1 06 05 00 f8 
進入c()后地址為0x06d100a0的內容(bob.c()的機器碼版本)如下:
0x06d100a0 55 8b ec 83 05 9c 53 97 00 04 90 5d c3 00 00 00 
進入b()后地址為0x06d100c0的內容(bob.b()的機器碼版本)如下:
0x06d100c0 55 8b ec 83 05 9c 53 97 00 03 90 5d c3 00 00 00
進入a()后地址為0x06d100e0的內容(bob.a()的機器碼版本)如下:
0x06d100e0 55 8b ec 83 05 9c 53 97 00 02 90 5d c3 00 00 00