[翻译]理解C#对象生命周期

时间:2022-03-14 08:46:42

看到网上的一篇讲C#对象生命周期(Object Lifetime)的文章,通俗易懂,而且有图,很适合初学者学习,就翻译过来了。后来发现这是Pro C# 2010 and the .NET 4 Platform的第八章中的一部分。(感谢 大乖乖 提醒)。文中的专业名词第一次出现时,括号里会标注对应的英文单词。
  请尊重作者劳动,转载请注明出处:。

----2012年11月15日修改----

  找到了文章的出处,并添加了最后一部分代码的截图。

----正文-----

  .NET 对象是在一个叫做托管堆(managed heap)的内存中分配的,它们会被垃圾回收器(garbage collector)自动销毁。
  在讲解之前,你必须知道类(class),对象(object),引用(reference),栈(stack)和堆(heap)的意思。

  一个类只是一个描述这种类型的实例(instance)在内存中布局的蓝图。当然,类是定义在一个代码文件中(在C#中代码文件以.cs作为后缀)。

  一个简单的Car 类,定义在一个叫做SimpleGC的C# Console Application中:

1 //Car.cs 2 public class Car 3 { 4 private int currSp; 5 private string petName; 6 public Car(){} 7 public Car(String name, int speed) 8 { 9 petName = name; 10 currSp = speed; 11 } 12 public override string ToString(){ 13 return string.Format("{0} is going {1} MPH", petName, currSp); 14 } 15 }

当一个类被定义好了,就可以使用C# new关键字来分配任意数量的这个类。

  需要理解的是,new关键字返回的是一个在堆上面的对象的引用,不是这个对象本身。这个引用变量存储在栈上,以便之后在程序中使用。
    当想在某个对象上调用成员函数时,对存储的这个对象的引用使用C# . 操作符:

1 class Program{ 2 static void Main(string[] args){ 3 //在托管堆上面创建一个新的Car对象。返回的是一个指向这个对象的引用 4 Car refToMyCar = new Car("Benz", 50); 5 //C# . 操作符用来在引用变量引用的对象上调用函数 6 Console.WriteLine(refToMyCar.ToString()); 7 Console.ReadLine(); 8 } 9 }  

  下图展示了类,对象和引用之间的关系

  对象生命周期的基础知识

  当你在创建C#应用程序时,托管堆不需要你的直接干预。实际上,.NET 内存管理的黄金原则很简单:
    使用new关键字在托管堆上申请一个对象
    一旦实例化后,当对象不再被使用的,垃圾回收器会销毁它。
    对于读者来说,下一个明显的问题是:
    “垃圾回收器怎么确定托管堆中的对象是不再被使用?”
    简洁的答案是:
     当你的代码不再使用堆上面的这个对象,垃圾回收器会将这个对象删除。

  假设你在程序的类里有一个方法分配了一个Car对象的局部变量:

1 static void MakeACar(){ 2 //如果myCar是Car对象的唯一引用 3 //它可能会在这个方法返回时被销毁 4 Car myCar = new Car(); 5 }

  注意:Car对象的引用 (myCar) 是在MakeACar()函数中直接创建的并且没有被传递到函数外部(通过一个返回值或者 ref/out 参数)。

  因此,一旦这个函数调用完成,myCar的引用不再可访问,并且和这个引用相关联的Car对象可以被垃圾回收了。但是,不能保证在MakeACar()函数调用完成后这个对象被立即从内存中销毁。
     在此时只能假设当CLR 执行下次垃圾回收时,myCar对象能够被安全的销毁。

  The CIL of new

  当C#编译器遇到new关键字,它会在函数实现中插入一个 CIL newobj指令。如果你编译当前的例子代码并使用ildasm.exe来浏览生成的代码,你会在MakeACar()函数中发现如下的CIL语句:

  在我们知道托管堆中的对象什么时候被移除的确切条件之前,仔细查看一下CIL newobj指令的作用。