揭开CLR神秘面纱-----第二步 垃圾回收之(制造垃圾)

 

阅读本章前,要求读者对值类型与引用类型有相当的了解

关于值类型与引用类型的深入讲解,请参见ANYTAO的[你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理  

----.NET应用程序初始化的时候所有的引用类型都会被分配到托管堆上面。而只有托管堆里面的资源才能被垃圾回收机制给管理起来。所以,我们要明白,CLR的垃圾回收机制并不是针对所有数据的。我们该手动释放的时候必须得要手动释放。

   我们从何得知这一结论?如何论证这一点?

   程序是如何在CLR中运行的呢?那我们CLR中垃圾回收机制又是怎么样运行的呢?我们是否需要再进一步去研究呢?我们如果不手动释放值类型,它们会常驻我们宝贵的内存空间吗?我不得不承认,这里牵扯到了很复杂的算法和操作系统及Win32编程的相关知识。这可能需要大篇幅的文字来说明这些问题。

   既然有垃圾回收,肯定就有垃圾制造者在制造垃圾。这些垃圾是如何被制造出来的,就是我们今天试图弄清楚的问题。希望能通过以下文字描述清楚,程序运行时各阶段的内存使用状态。

   请看下面的代码:

很明显,上面定义了一个类,一个枚举,一个结构,这是一段表面看似很普通的代码,但其实骨子里却大有乾坤。我们再次打开ILdasm来看下它的真面目。


     可以看的出,两个类都有.ctor,就是说,两个类都有构造函数。点开会发现原来是继承自Object的构造函数。这也就解释了为什么我们定义类的时候就算不显示声明构造函数,系统也会自动给你声明构造函数。功劳来自于CLR.

    细心的朋友又会发现另外一个问题,那这里这个结构明明没有看见构造函数,那它为什么也能new呢?关于这里,可以参考anytao关于new的文章

我这里谈谈自己的看法。我们点开Main函数。会看到如下内容

     

   可以看到,在init的时候,里面传入了11个参数。分别就是我们代码里使用的11个对象。Init指令起的作用是“init specifies that the variables must be initialized to the default values for their respective types” init 指定的变量必须被各自类型的默认值初始化--原文地址

    继续我们上面的问题,结构没有构造函数为什么也能new呢?分析IL代码,发现new class的时候也就是new 引用类型的时候,IL的指令是newobj,并且还instance了相应的构造函数,new struct的时候,IL的指令却是initobj。这似乎就是问题的根源所在。
  先说说newobj指令吧,执行该指令的时候,他会将我们指定的类型实例化,而且根据需要会将新类型中的成员初始化一次,再把这个对象送到托管堆上。然后再根据参数调用构造函数。构造函数对该类型做的操作就是在托管堆中进行了,最后在线程堆上只是把这个对象的引用压栈。(我们的托管堆上其实存在一个指针。该指针就是我们垃圾回收的最高检察官。关于他,咱们以后再做描述。)

    那我们的initobj做了什么工作呢?MSDN上给出的解释很简单。该指令就是初始化值类型。因为编译后,非primitive type的值类型已经存在于线程堆栈上。但是还没有初始化。所以需要做这一步操作。而所有的primitive type在编译时会初始为0.这也就是为什么char和MyStruct同为值类型而char没有init的原因了。    

    通过上面的描述,我们应该可以清晰的看出.NET CLR中的运行过程,对象的创建是一个递归的过程。恰恰也是咱们通常所说的Builer模式的一个体现。至此,我们的“垃圾”都已经制造完毕,下一步可以讨论生成的垃圾该如何回收了。这一步最重要的理解在于初始化。
                                                        MSIL速查手册下载,感谢无私奉献

版权声明:原创技术文章,如需转载,请声明出处。不得用于任何商业形式活动,否则将追究法律责任。

PS:非常感谢sumtec@beijing,剑在上海^^,两位网友提出的意见。

 

posted on 2007-07-17 20:27  刘荣华  阅读(2733)  评论(23编辑  收藏  举报

导航