2014-10-07 46 views
1

最近,我讀了Jon Skeet's Blog談論C#對象的佔用空間和開銷。我寫了下面的代碼來複制他的實驗。結構/值類型內存分配和解除位置

class Pixel 
{ 
    private byte _r; 
    private byte _g; 
    private byte _b; 
    public int x { get; set; } 
    public int y { get; set; } 

    public System.Windows.Media.Color Color 
    { 
     get { return System.Windows.Media.Color.FromRgb(_r, _g, _b); } 
    } 
} 

static void Main(string[] args) 
{   
    size = 1000; 
    var array3 = new Pixelsize]; 
    before = GC.GetTotalMemory(true); 
    for (int i = 0; i < size; i++) 
    { 
     array3[i] = new Pixel(); 
    } 
    after = GC.GetTotalMemory(true); 
    Console.WriteLine("Pixel is {0} bytes", (after - before)/size); 
} 

到目前爲止好,該程序報告"Pixel is 15 bytes",這是8個字節基地+ 4個字節+ 1 + 1 + 1 = 15個字節。

然後我想知道:struct實例是否具有與class實例相同的開銷。所以我將Pixel更改爲struct

struct Pixel 
{ 
    private byte _r; 
    private byte _g; 
    private byte _b; 
    public int x { get; set; } 
    public int y { get; set; } 

    public System.Windows.Media.Color Color 
    { 
     get { return System.Windows.Media.Color.FromRgb(_r, _g, _b); } 
    } 
} 

現在,程序報告"Pixel is 0 bytes"。進入代碼,我發現afterbefore相同。所以struct是一個值類型,它是從堆棧中分配的。對?除此之外,當我檢查寄存器「ESP」根本沒有改變。所以它不是從堆棧中分配的?

查看TaskManager,演示程序的內存使用量在分配後增加了8000個字節。這8000字節來自哪裏?

最後,由於GC未取消內存分配,我該如何取消分配此內存?我試圖將分配代碼放在一個塊內,並希望array3超出範圍時,這些內存將被釋放。但是,內存使用情況沒有改變。我在這裏得到內存泄漏嗎?

static void Main(string[] args) 
{   
    { 
    size = 1000; 
    var array3 = new Pixelsize]; 
    before = GC.GetTotalMemory(true); 
    for (int i = 0; i < size; i++) 
    { 
     array3[i] = new Pixel(); 
    } 
    after = GC.GetTotalMemory(true); 
    Console.WriteLine("Pixel is {0} bytes", (after - before)/size); 
    } 
    //Expect the memory to be released here, but nothing happened. 
} 
+0

這意味着什麼? – 2014-10-07 03:40:19

回答

2
  1. 當您分配引用類型的Array,在函數內部..參考到陣列本身可被存儲在預先分配的堆棧幀(即4/8字節32/64位) 。 1000個元素的實際分配在堆上,每個元素也是4/8字節。此外,當你調用new Pixel()時,類的實例被分配,並且因爲它們的引用被存儲在數組中而保持活動狀態。

  2. 當您將其更改爲值類型的Array時,在函數內部。對數組本身的引用可以存儲在預先分配的堆棧幀上(即32/64位爲4/8字節)。 1000個元素的實際分配在堆上,每個元素x字節的大小,其中x是值類型的大小。任何分配給數組元素的值,都被複制,每個字節.. 數組元素沒有引用任何值。

既然你分配值類型的數組,調用before = GC.GetTotalMemory(true);,前後沒有看到任何配置差異之前。

換句話說,在類情況下,分配是在線路array3[i] = new Pixel();(在堆) 但在sruct的情況下,分配是在線路var array3 = new Pixel[size]; 對於該結構,new Pixel();使用上堆疊中的很小的空間,但隨後您將該值複製到堆中數組的預分配空間中......並且最有可能在每次迭代中重用該堆棧空間。

如果考慮一組int而不是Pixel數組,可能會更容易想到整個事情。除了它們的大小不同之外,intPixel(定義爲結構)之間的機制將相同。