打自己脸——有关.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上。

一些关于闭包的废话

继函数形参传值传指针、GC机制的Blog后,貌似这又是说烂的问题?没办法,最近在回顾各种知识,发现有联系的就拿出来写一写,省得日后发霉。如果你现在对闭包还不是很了解,可以阅读一下学习Javascript闭包(Closure)深入理解JavaScript闭包(1)理解 JavaScript 闭包三篇文章。难度依次上升。

JavaScript的闭包

闭包的概念已经比科学发展观都深入人心了,简单说它就是“具有独立作用域并且可以引用外层作用域的对象”。例如C#中的匿名函数和Lambda表达式就是基于的闭包概念,其他的高级语言,类似Java、JavaScript和Python等也都有闭包的应用。当然也不要忘了JavaScript的内存泄露主因之一也是闭包,所以先看JS好了。

还是先从代码入手,下面两段代码是从阮一峰老师的那篇闭包讲解中摘取的最后习题,那么现在提问,两段代码分别alert什么内容?

var bar = "Global";
  var object1 = {
    bar : "local",
    foo : function(){
      return function(){
        return this.bar;
      };
    }
  };
  alert(object1.foo()());

//我是分隔线

  var object2 = {
    bar : "local",
    foo : function(){
          var that = this;
      return function(){
        return that.bar;
      };
    }
  };
  alert(object2.foo()());

Read more >>>