2015-02-12 132 views
1

我知道我們不初始化靜態類。我們可以按類名使用它們。但它必須位於靜態內存中,以便我們可以使用它們。什麼時候靜態類被置於靜態內存中?它是在運行時還是延遲加載?.NET中的靜態類初始化

+1

你檢查:https://msdn.microsoft.com/en-us/library/79b3xss3.aspx? – 2015-02-12 06:30:42

+0

靜態類只是「存在」。它聲明的所有字段在第一次加載程序集時會在內存中給出空間。但是,在執行靜態構造函數之前,這些字段的狀態和值不會被初始化。 – 2015-02-12 06:49:45

+0

我不知道你認爲「靜態內存」是什麼,也不知道爲什麼靜態類必須放在它裏面。 – 2015-02-12 07:26:59

回答

2

我懷疑你是懶惰的。我不知道你的「靜態記憶」是什麼意思,但我認爲這是一個關於靜態成員的運作以及它們如何與記憶聯繫的非常可行的問題。靜態成員將被放置在「常規內存」中,就像任何實例成員一樣。

靜態實例在加載時放置在堆上(雖然它是一種不同於實例的堆,稱爲High Frequency Heap),但在使用前不會初始化。取決於你擁有哪些靜態成員(也就是它們的類型,以及這些類型是引用還是值類型),這可能是一個很大或很小的問題。例如,如果您有一個包含200個元素的字節數組,那麼只有當您使用它時纔會加載(少於它最初將保存的null)。但是如果你有200個字節的元素,每個元素都會在開始時放入內存中。

換句話說,使用前,ClassA將佔用比ClassB更少的內存,但在使用過程中,它們將是類似的。當然,不完全一樣,但你可以想象我想要說明的一點。

static class ClassA 
{ 
    public static byte[] bytes = new byte[] { 1, 12 }; 
} 

static class ClassB 
{ 
    public static byte ByteA = 1; 
    public static byte ByteB = 12; 
} 

這是因爲在ClassAbytes初始值是null,但在ClassB,也有從進程的開始存儲兩個值,這兩者正好是0。你可以想象這會如何與其他參考值與值類型的字段相結合。

GC運行時會發生另一個區別。如果我沒有弄錯(儘管我不相信這是特定行爲),靜態成員不會被垃圾收集,直到過程終止。除此之外,您可以(鬆散地)將它們視爲類的一個簡單的,編譯器強制的單例實例。


最簡單的方法來確定值的這種「懶加載」將放置一個副作用的呼叫靜態構造函數。喜歡的東西,

public class Foo 
{ 
    static Foo() 
    { 
     Console.WriteLine("In static constructor!"); 
    } 

    public static void Bar() 
    { 
     Console.WriteLine("In Bar!"); 
    } 
} 

放在一個小控制檯應用程序的情況下,你可以很容易地確定何時調用構造函數,並推斷這件事情,就是當靜態成員被放置到內存中。

void Main(string[] args) 
{ 
    Console.WriteLine("First line!"); 
    Foo.Bar(); 
    Console.WriteLine("Last line!"); 
} 

這個程序outputs

First line! 
In static constructor! 
In Bar! 
Last line! 

您還可以找到在C#5.0規範(§10.12靜態構造函數)這一信息,

對於封閉類類型的靜態構造函數在給定的應用程序域中最多執行一次。靜態構造函數的執行由應用程序域中發生的以下第一個事件觸發:

  • 創建類類型的實例。
  • 引用任何類類型的靜態成員。

在外行的話來說,靜態成員初始化您第一次使用靜態類。如果它從未使用過,那麼它們永遠不會被初始化。


爲了將我的觀點帶回家,讓我們看看最後一對方法。

static class Foo 
{ 
    public static int X = 2; 
    public static byte[] Bytes = new byte[] { 24, 66 }; 
} 
class Bar 
{ 
    public int X = 2; 
    public byte[] Bytes = new byte[] { 24, 66 }; 
} 

單步穿過(在IL級別),與這些類的交互會非常不同。

考慮一個方法:

void Main(string[] args) 
{ 
    // Foo is put into memory when the process spins up: 
    //  X : 0 
    //  Bytes : null 

    Console.Write(Foo.X); // During this line, Foo is initialized 
          //  X : 2 
          //  Bytes : 24, 66 

    Console.Write(Foo.X); // Nothing happens 

    Bar b = new Bar(); // During this line, Bar is put into memory, then initialized 
         //  X : 0, then "instantaneously" becomes 2 
         //  Bytes : null, then "instantaneously" becomes 24, 66 

    Console.Write(b.X); // Nothing happens 

    // Eventually, b will be cleared from memory by the garbage collector 
    // but Foo will not be, until the program closes. This sample is much 
    // too small to display that difference. 
} 
+0

謝謝你。那回答了我的問題。 – kibs 2015-02-13 03:48:13

+0

靜態內存是HFH的同義詞:當進程加載到RAM中時,我們可以說內存大致分爲三個區域(在該進程內):Stack,Heap和Static(在.NET中,它實際上是堆內的一個特殊區域,只被稱爲高頻堆)http://www.codeproject.com/Articles/15269/Static-Keyword-Demystified – kibs 2015-02-13 03:50:44