.NET執行時之書(Book of the Runtime,簡稱BotR)是一系列描述.NET執行時的檔案,2007年左右在微軟內部建立,最初目的是為了幫助其新員工快速上手.NET執行時;隨著.NET開源,BotR也被公開了出來,如果想深入理解CLR,這系列文章不可錯過。
BotR系列目錄:
[1] CLR型別載入器設計(Type Loader Design)
[2] CLR型別系統概述(Type System Overview)
原文:https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/type-system.md
作者: David Wrighton - 2010
翻譯:幾秋 (https://www.cnblogs.com/netry/)
CLR型別系統是我們在ECMA規範+擴充套件中描述的型別系統的表示形式。
該型別系統是由一系列資料結構(其中一些在BotR的其它章節有描述)和操作這些資料結構的演演算法組合而成,它不是通過反射暴露出來的型別系統,儘管反射確實依賴於這個系統。
由型別系統維護的主要資料結構是:
由型別系統包含的主要演演算法是:
還有很多輔助資料結構和演演算法為CLR的其餘部分提供各種資訊,但它們對於整個系統的理解並不那麼重要。
型別系統的資料結構通常被各種演演算法所使用。本檔案不會涉及型別系統演演算法(),但是它會試圖描述各種主要資料結構。
型別系統大體上是給CLR中很多部分提供服務,多數核心元件都對型別系統的行為有某種形式的依賴性。下圖描述了影響型別系統的通用資料流,它不是很全面,但是指出了只要的資訊流。
型別系統的主要依賴關係如下:
型別系統有3個主要元件依賴於它:
核心型別系統資料結構是表示實際載入型別的資料結構(例如,TypeHandle, MethodTable, MethodDesc, TypeDesc, EEClass)和允許在載入型別後找到它們的資料結構(例如,ClassLoader, Assembly, Module, RIDMaps)。
載入型別的資料結構和演演算法在BotR的Type Loader 和 MethodDesc章節中有討論。
將這些資料結構繫結在一起是一組功能, 允許JIT/Reflection/TypeLoader/stackwalker去查詢現存型別和方法,一般的想法是,這些搜尋應該很容易地由ECMA CLI規範中指定的後設資料令牌/簽名(metadata tokens/signatures)驅動。
最後,當合適的型別系統資料結構被找到,我們有演演算法從型別中收集資訊,有and/or比較兩個型別。可以在 Virtual Stub Dispatch找到這種演演算法的一個特別複雜的例子。
型別轉換演演算法(casting algorithm)是型別系統中的典型演演算法,在受控程式碼的執行過程中大量使用這種演演算法。
這個演演算法至少有4個獨立的入口(entry point),選擇每個入口都是為了提供不同的快速路徑,希望能夠實現最佳的效能。
除了最後一個之外,每個實現都進行了優化,以便在不完全通用的情況下提高效能。例如,「一個型別能否被轉換成一個父類別?」就是 「一個物件能否被轉換成一個非型別等價的非陣列型別」的變體,它通過單迴圈遍歷一個單連結串列實現。這隻能搜尋可能的轉換操作的一個子集,但可以通過檢查試圖強制轉換的型別來確定是否是合適的集合,這個演演算法在jit helper JIT_ChkCastClass_Portable
中實現。
假設:
在型別系統中很多演演算法遵循這種常見模式。型別系統通常用於查詢型別,這可以通過任意數量的輸入觸發,如JIT、反射、序列化、遠端呼叫等等。在這些情況下,對型別系統的基本輸入是:
這個演演算法必須首先解碼識別符號。對於搜尋一個型別的場景,令牌可能是typedef令牌、typeref令牌、typespec令牌,或者是一個字串。這些不同種類的識別符號都會將導致不同形式的查詢。
從這個設計中可以明顯看出一些搜尋演演算法在型別系統中的共同特點:
除了這個總體設計,在此基礎上,還有一些額外的需求:
此搜尋演演算法是 JIT期間使用的典型程式。它具有許多共同的特徵:
這使我們能夠滿足效能要求,以及使用基於IL的JIT所必需的特徵。
垃圾回收器要有關型別範例分配在GC堆上的資訊,這是通過一個指向型別系統資料結構(MethodTable)的指標來完成,該MethodTable位於每一個託管物件的開頭。附著到這個MethodTable之上的,是一個描述型別範例GC佈局的資料結構。該佈局有兩種形式(一般型別和物件陣列是一種,值型別陣列是另一種)。
堆疊遍歷器/GC堆疊遍歷器在兩種情況下要求型別系統輸入:
由於各種原因,包括希望延遲載入型別,以及避免生成多個版本的程式碼(僅通過相關的gc資訊不同),CLR當前需要遍歷堆疊上的方法簽名,這種需求很少得到滿足,因為它要求在特定的時刻執行堆疊遍歷,但是為了滿足我們的現實目標,當遍歷堆疊的時候,必須能夠遍歷簽名。
堆疊遍歷器以大約 3 種模式執行:
在GC和分析工具遍歷堆疊的情況,由於執行緒被掛起,分配記憶體或佔用大多數鎖是不安全的。這導致我們開發了一條通過型別系統的路徑,可以依賴它來遵循上述要求。型系統實現此目標所需的規則是:
這是通過一系列廣泛而複雜的強制措施來實施的,包括型別載入器、NGEN映象生成過程和JIT。
型別系統資料結構是儲存到NGEN映象中的核心部分,不幸的是,這些資料結構邏輯內有指向其它NGEN映象的指標。為了處理這種情況,型別系統資料結構實現了一個稱為恢復(restoration)的概念。
在恢復期,當第一次需要型別系統資料結構時,該資料結構用正確的指標固定, 這與型別載入級別有關,請看前篇CLR型別載入器設計
還存在一個預恢復(pre-restored)資料結構的概念,這意味著資料結構在NGEN映象載入時足夠正確(在intra-module指標和預先載入型別修正之後),資料結構可以按原樣使用。此優化要求將NGEN映象「硬繫結」("hard bound")到其依賴程式集,詳情請檢視NGEN相關檔案。
型別系統是實現域中性載入的核心部分,它通過在AppDomain建立時啟用LoaderOptimization選項暴露給使用者。Mscorlib始終作為域中性載入,此功能的核心要求是型別系統資料結構不能要求指向特定域狀態(domain specific state)的指標,這主要表現在圍繞靜態欄位和類建構函式的需求中。特別是,由於這個原因,一個類別建構函式是否已經執行不是核心MethodTable資料結構的一部分。並且有一種機制來儲存附加到DomainFile資料結構而不是MethodTable資料結構。
型別系統的主要部分位於:
主要入口函數是BuildMethodTable、 LoadTypeHandleThrowing、CanCastTo*、 GetMethodDescFromMemberDefOrRefOrSpecThrowing、 GetFieldDescFromMemberRefThrowing、 CompareSigs和 VirtualCallStubManager::ResolveWorkerStatic.
作者: 幾秋
出處: https://www.cnblogs.com/netry/p/clr-type-system-chinese.html
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。