方法 (Method) 是一種類型定義,所以,它被存放在 Type Object 上,Type Object 是一個被分配在託管堆上的特殊類型,在同一個 AppDomain 中,每一個類型,都對應一個全局的 Type Object。每個引用類型的實例,都包含一個指向它的直接類型的 Type Object 的指針,每個 Type Object 也存在類似的指針,用來標識它的直接父類型的 Type Object。
當調用靜態方法時,CLR 會根據方法調用去尋找其對應的 Type Object,然後,把方法 JIT,JIT 之後的方法是本機代碼,可以直接運行,然後,這部分代碼被加載進入內存,方法的參數被加載進入當前執行棧,原來的執行上下文地址也被記錄到執行棧;方法開始執行,執行完後,執行棧中的返回地址被讀出,然後 CLR 利用本機跳轉指令,跳轉到該返回至繼續執行。
當調用實例方法時,CLR 會根據實例的 Type Object 指針找到對應的 Type Object,然後,把方法 JIT,JIT 之後的方法是本機代碼,可以直接運行,然後,這部分代碼被加載進入內存,該實例對象,以及方法的參數被加載進入當前執行棧 (實例對象永遠是第一個參數,即 arg0,利用 ldarg0 指令進行讀取),原來的執行上下文地址也被記錄到執行棧;方法開始執行,執行完後,執行棧中的返回地址被讀出,然後 CLR 利用本機跳轉指令,跳轉到該返回至繼續執行。
如果方法已經被 JIT 過,則不會被第二次 JIT。
方法在 IL 中是以字節流的形式存在的,所以,它仍然會佔據內存。
方法 JIT 之後會被駐留在該進程的地址空間裏面,因此,它也會在運行時佔據內存。
方法的元數據存放在程序集 MethodRef 以及 MethodDef 表中。
定義在值類型上的實例方法就比較麻煩了,大家有興趣可以想想它怎麼執行的。因爲值類型沒有 Type Object 指針。
如果值類型實現一個接口,在執行接口的方法實現的時候就更加麻煩了,大家也可以想想,歡迎討論!
最後,
大家都以爲“ 靜態方法在堆上分配內 存,實例方法在堆棧上”
這句話完全不靠譜,不要被迷惑了。。。只要提到方法,它就一定在 Type Object 上,也就是被分配在託管堆上。