2014-10-07 45 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(定义为结构)之间的机制将相同。