对象的生命周期

  • .net对象是被分配到一块叫做托管堆(managed heap)的内存区域上。
  • new一个对象,返回的是一个指向堆上的引用,而不是真正的对象本身,这个对象保存在栈上。
  • 内存管理法则:1、new后不用再管。2、如果托管堆上没有足够的内存,就会进行垃圾回收。

  • 应用程序根:根就是一个存储位置,保存着对堆上一个对象的引用,可以是以下几种类别:
  1.   全局对象的引用(虽然在c#不允许,但是CIL的确允许分配全局对象)
  2.   静态对象/静态字段的引用
  3.   应用程序的代码库的局部对象的引用
  4.   传递进一个方法的对象参数的引用
  5.   等待被终结的对象的引用
  6.   任何引用对象的CPU寄存器

    在一次垃圾回收过程中,运行库将检查托管堆上的对象,判断应用程序是否仍然可达到它们,即是否有根的。为此,CLR将建立一个对象图,代表堆上可达的每一个对象。没有在对象图中的即为被标记为终结的对象,会从内存中清除,然后,调整可达的对象在内存上的位置,组成一个无冗余并且压缩过的堆。

  • 对象的代。

      要寻找不可达的对象,如果CLR逐个检查托管堆的没一个对象,很明显,效率很低。

      每个对象都会被指定为属于某“代”,设计思路很简单:对象在堆上存在的时间越长,它就更可能应该保留,而刚加的可能很快不可达。

      有三种

        第0代:从没有被标记为回收的新分配的对象。

          第1代:在上一次垃圾回收中没有被回收,但被标记为“回收”的对象,只是因为已经获取到足够的内存而没有被删除。

        第2代:在一次以上垃圾回收中没有被回收的对象。

            总结垃圾回收过程如下:在内存不足的情况下,第一次GC,先搜寻第0代,删除一些对象,而没有被删除的升级为第1代;第二次GC,如果那些刚升级为第1代的还不需要被删除(即可达或者已经获取足够内存),那么再次升级为第2代。以此类推。记住准则为先搜寻第0代,逐代删除不可达,并将没有删除的升级。并且第2代无法继续升级,只能停留在第2代。也可理解为,尽快删除一些新的对象,避免打扰旧的对象。



  • System.GC类型

    极少使用这个类型。只有在创建非托管资源的类型时,才需要在代码中直接使用这个类型。


   通过编程使用GC.Collect()强制进行可能会有好处。说得更明确就是:

  • 应用程序将要进入一段代码,后者不希望被可能的垃圾回收中断。
  • 应用程序刚刚分配非常多的对象,你想尽可能多地删除已获得的内存。

  

维护内部非托管资源的托管类的手段:Finalize()--终结Dispose()--处置

非托管资源:原始的操作系统文件句柄,原始的非托管数据库连接,非托管内存或其他非托管资源。

Finalize()特性:

  • 重写Finalize()的唯一原因是,c#类通过PInvoke或复杂的COM互操作性任务使用了非托管资源(典型的情况是通过System.Runtime.InteropServices.Marshal类型定义的各成员)注:PInvoke是平台调用服务。
  • object中有finalize方法,但创建的类不能重写此方法,若Overide会报错,只能通过析构函数来达到同样的效果。
  • Finalize方法的作用是保证.NET对象能在垃圾回收时清除非托管资源。
  • 在CLR在托管堆上分配对象时,运行库自动确定该对象是否提供一个自定义的Finalize方法。如果是这样,对象会被标记为可终结的,同时一个指向这个对象的指针被保存在名为终结队列的内部队列中。终结队列是一个由垃圾回收器维护的表,它指向每一个在从堆上删除之前必须被终结的对象。
  • 注意:Finalize虽然看似手动清除非托管资源,其实还是由垃圾回收器维护,它的最大作用是确保非托管资源一定被释放
  • 在结构上重写Finalize是不合法的,因为结构是值类型,不在堆上,Finalize是垃圾回收器调用来清理托管堆的,而结构不在堆上。

Dispose()特性:

  • 为了更快更具操作性进行释放,而非让垃圾回收器(即不可预知)来进行,可以使用Dispose,即实现IDispose接口.
  • 结构和类类型都可以实现IDispose(与重写Finalize不同,Finalize只适用于类类型),因为不是垃圾回收器来调用Dispose方法,而是对象本身释放非托管资源,如Car.Dispose().如果编码时没有调用Dispose方法,以为着非托管资源永远得不到释放。
  • 如果对象支持IDisposable,总是要对任何直接创建的对象调用Dispose(),即有实现IDisposable接口的类对象都必须调用Dispose方法。应该认为,如果类设计者选择支持Dispose方法,这个类型就需要执行清除工作。记住一点,如果类型实现了IDisposable接口,调用Dispose方法总是正确的。
  • .net基类库中许多类型都实现IDisposable接口,并使用了Dispose的别名,其中一个别名如IO中的Close方法,等等别名。使得看起来更自然。
  • using关键字,实际内部也是实现IDisposable方法,用ildasm.exe查看使用了using的代码的CIL,会发现是用try/finally去包含using中的代码,并且在finally中调用dispose方法。

个人总结:

相同点:

  • 都是为了确保非托管资源得到释放。

不同点:

  • finalize由垃圾回收器调用;dispose由对象调用。
  • finalize无需担心因为没有调用finalize而使非托管资源得不到释放,而dispose必须手动调用。
  • finalize虽然无需担心因为没有调用finalize而使非托管资源得不到释放,但因为由垃圾回收器管理,不能保证立即释放非托管资源;而dispose一调用便释放非托管资源。
  • 只有类类型才能重写finalize,而结构不能;类和结构都能实现IDispose.原因请看上面Finalize()特性

两个方式有优有劣,可有一个好的解决方案来平衡两个方法,下篇介绍。P209

posted @ 2011-08-12 23:11  苏先森1989  阅读(652)  评论(2编辑  收藏  举报