承接[上集](第二十四回)與[中集](第二十五回),本文深入 JIT 編譯階段,揭示元數據與 IL 如何協同驅動程式執行。核心流程:類別載入 → 方法表建立 → 首次呼叫觸發 JIT → IL 轉換原生碼 → 後續直接執行。
public class Base {
public void M() { Console.WriteLine("M in Base"); }
public virtual void N() { Console.WriteLine("N in Base"); }
}
public class Three : Base {
private static int ID { get; set; }
public override void N() { Console.WriteLine("N in Three"); }
public void M() { Console.WriteLine("M in Three"); M1(); }
public void M1() { Console.WriteLine("M1 in Three"); }
}
static void Main() {
Base three = new Three();
three.M(); three.N();
}
Class Loader 從元數據表 (TypeDef, MethodDef 等) 讀取類型資訊,建立 CORINFO_CLASS_STRUCT 結構,包含:
Three 的方法表結構示意:
透過 SOS 擴充指令驗證:
!dumpheap -type Three
!dumpmt -md
輸出顯示 MethodDesc Table,每個方法對應一個 MethodDesc 結構,初始指向 IL 代碼與 PreJitStub(JIT 觸發器)。
每個 MethodDesc 初始包含:
PreJitStub 的呼叫指令首次呼叫流程:
PreJitStubMethodDesc 中的 JIT 指令(插入 jmp)後續呼叫:直接跳轉至原生碼,零 JIT 開銷。
three.N() 透過 VTable 分派:
Three.N 的 MethodDesc非虛方法 M() 則直接透過 MethodDesc 靜態繫結。
IL 指令透過 4 位元組 Metadata Token 參考元數據表:
0x06 = MethodDef).method private hidebysig static void Main() cil managed
{
IL_0003: newobj instance void .../One::.ctor() /* 06000007 */
IL_000b: callvirt instance void .../One::set_ID(int32) /* 06000006 */
}
運行時可透過 Type.MetadataToken 取得 Token 值。
元數據描述靜態結構,IL 闡釋動態執行,兩者在 JIT 編譯階段緊密協作:
後續主題將延伸至:組件與模組、應用程式域、CLR 載入流程、JIT 深度解析、方法呼叫慣例、反射機制等,構建完整 CLR 運行時知識圖譜。