有一件事我一直想知道static fields
/constructors
。CLR如何知道靜態字段是否已經被初始化?
static class
第一次初始化它的一個字段被引用,這很容易。
但CLR如何知道這是第一次?
有一件事我一直想知道static fields
/constructors
。CLR如何知道靜態字段是否已經被初始化?
static class
第一次初始化它的一個字段被引用,這很容易。
但CLR如何知道這是第一次?
static class
第一次初始化其中一個字段被引用,這很容易。
不,不是那麼簡單。忽略方法調用,static
類必須在它的第一個字段被初始化之前初始化,如果它沒有標記爲beforefieldinit
。如果標記爲beforefieldinit
,則可以在此之前進行初始化。 (Jon Skeet有an article with lots more information about beforefieldinit
。)
這是如何影響何時檢查類初始化的?由於CLR使用JIT編譯,所以它會在JIT編譯方法時檢查類的初始化。如果該類標記爲beforefieldinit
且尚未初始化,則JIT編譯器會立即對其進行初始化。然後它實際上編譯該方法,它可以假定該類已經被初始化,所以不需要檢查。
沒有beforefieldinit
,如果類尚未初始化,JIT編譯器必須發出代碼,以便在每次可能的第一次字段訪問之前檢查初始化。但是如果這個類已經被初始化了,並且另一個方法正在被JIT編譯,JIT編譯器就不必再在那裏發出檢查了。
在某些情況下,這會對性能產生負面影響。從上面很清楚,爲了防止這種情況發生,您需要確保有問題的類別標記爲beforefieldinit
。而從C#做到這一點的方式是沒有static
構造函數,只使用static
字段初始值設定項。
有趣。如果潛在的第一個字段Access在整個解決方案中大量依賴並且嚴重依賴於外部輸入,我想這會特別成問題。非常感謝您的深入解答。 –
是的,非常感謝。雖然我不得不承認,但在訪問某個字段時(例如,與C++比較),每次檢查代價都很昂貴。 –
要清除此類檢查,不會在每次訪問該類時執行此檢查。加載使用類時會檢查類加載狀態。如果您的類A使用B.foo的靜態屬性,那麼當使用A時,將檢查它的依賴關係。所以當A初始化時,B也會被初始化。對B.foo的訪問不會產生這樣的檢查。 – Kolja
我已經對答案做了一些編輯,以便在此檢查發生時更加清楚。 – Kolja