struct / value类型内存分配和deacllocation

时间:2014-10-07 03:24:48

标签: c# memory memory-management

最近,我阅读了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是一个值类型,它是从堆栈中分配的。对?当我检查寄存器&#34; ESP&#34;根本不会改变。所以它不是从堆栈中分配的?

查看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. 
}

1 个答案:

答案 0 :(得分:2)

  1. 在函数内部分配Array引用类型时。对数组本身的引用可以存储在预先分配的堆栈帧上(即32/64位的4/8字节) 。 1000个元素的实际分配在堆上,每个元素再次为4/8个字节。此外,在调用new Pixel()时会分配实例类,并保持活动状态,因为它们的引用存储在数组中。

  2. 当你在函数内部将它更改为值Array的值类型时。对数组本身的引用可以存储在预先分配的堆栈帧上(即32/64的4/8字节)位)。 1000个元素的实际分配在堆上,每个元素x个字节的大小,其中x是值类型的大小。分配给数组元素的任何值都会被复制,每个字节都被复制。数组元素没有引用任何值。

  3. 由于您在调用before = GC.GetTotalMemory(true);之前分配了值类型数组,因此在分配之前和之后都没有看到任何差异。

    换句话说,在类的情况下,分配在行array3[i] = new Pixel();(在堆上) 但是在sruct的情况下,分配在var array3 = new Pixel[size];行 对于结构体,new Pixel();在堆栈上使用了一点空间,但是然后将该值复制到堆中数组的预分配空间...并且很可能在每次迭代时重用该堆栈空间。

    如果考虑int的数组而不是Pixel的数组,可能更容易思考整个问题。除了它们的大小差异之外,{{1}之间的机制和int(定义为struct)将是相同的。