打自己脸——有关.net对象分配

之前发过的那篇.NET的GC机制和GC知识学习里面有一选项为:

A. 值类型的对象会被分配到Stack上,而引用类型的对象会被分配到Large Object Heap上。

今天在写前一篇Blog时候,发现这句话其实大错特错,虽然这个选项本身就是故意加入了错误,作为选择题答案出现,但此错非彼错。还好题目没有正式使用,不然会被人打脸打得啪啪响。在尚未被人打脸之前还是先自打比较好。先看前半句:

值类型的对象会被分配到Stack上

拿值类型中最常被提到用来和引用类型相比较的Struct来说,它一定分配到Stack上吗?当然不是。这一点题目已经涉及到了,就是大于85k的对象会分配到LOH上。但忽略的一点是一般尺寸的值类型对象也有可能分配到Heap上:

  • 类成员变量,就算是值类型也是在Heap上分配;
  • 局部值类型变量,涉及到和闭包相关的特性时。

第一条就不说了。第二条例如下面这个例子,i和j都是局部值类型变量,他们都在Stack上吗?

private void Form1_Load( object sender, EventArgs e ) {
    int j = 0;
    int i = 0;
    Func foo = () => {
        return i;
    };
}
当然是否定的,拿出il代码看一下就明了:从声明上来看,j的确是在Stack上没错,但i却是依附在一个编译器自动生成的类<>c__DisplayClass3上面;进行赋值时候i用的是stfld而不是stloc。这也就是说对于闭包涉及到的局部变量而言,CLR会将其依附到一个自动生成的类上进行存取操作,那么它自然就不分配到Stack上了。
.method private hidebysig instance void  Form1_Load(object sender,
class [mscorlib]System.EventArgs e) cil managed
{
// 代码大小       31 (0x1f)
.maxstack  3
.locals init ([0] int32 j,
[1] class [System.Core]System.Func`1 foo,
[2] class WindowsFormsApplication1.Form1/'<>c__DisplayClass3' 'CS$<>8__locals4')
IL_0000:  newobj     instance void WindowsFormsApplication1.Form1/'<>c__DisplayClass3'::.ctor()
IL_0005:  stloc.2
IL_0006:  nop
IL_0007:  ldc.i4.0
IL_0008:  stloc.0
IL_0009:  ldloc.2
IL_000a:  ldc.i4.0
IL_000b:  stfld      int32 WindowsFormsApplication1.Form1/'<>c__DisplayClass3'::i
IL_0010:  ldloc.2
IL_0011:  ldftn      instance int32 WindowsFormsApplication1.Form1/'<>c__DisplayClass3'::'
b__2'()
IL_0017:  newobj     instance void class [System.Core]System.Func`1::.ctor(object,
native int)
IL_001c:  stloc.1
IL_001d:  nop
IL_001e:  ret
} // end of method Form1::Form1_Load

另外漏了一点,在更正:值类型并不总是分配在栈上一文中提到,还有一种会被分配到Heap上的值类型:

  • 数组内的值类型对象,会被一同分配到Heap上。

One Response to “打自己脸——有关.net对象分配”

  1. […] Heap的SOH(Small Object Heap)上(update:错误不仅如此,请参考打自己脸——有关.net对象分配,超过85KB对象会被分配到LOH。至于85K这个Magic […]