Finalize 和 Dispose 的分析

一. 构建可终结对象

//System.Object
  public class Object
        {
            ...
            protected virtual void Finalize() { }
        }

当在自定义类中重写Finalize()时,垃圾回收器就可以在从内存中删除这个对象前,调用对象的Finalize()方法。 注意,结构类型是不可以重写Finalize()的。

这个成员被定义为被保护的,所以不能通过点操作符从类实例中直接调用一个对象的Finalize()方法。

实际上,对象并不显示的调用Finalize()方法,其主要是由垃圾回收器自动调用。

这里要明确一个概念,就是Finalize()调用的时间。

Finalize()的调用发生在一次“自然的”垃圾回收或用程序通过GC.Collect()强制回收的过程中。

其应该以类的析构函数(以~为前缀,和构造函数相似。但其不接受访问修饰符,不授受参数,也不能被重载)的形式进行重写,否则编译错误。

下面来说一下终结的具体细节:

当在托管堆上分配对象时,运行库自动确定该对象是否提供一个自定义的Finalize()方法。 如果是这样,对象标记为可终结的,同时一个指向这个对象的指针被保存在名为

“终结队列”的内部队列中。终结队列是一个由垃圾回收器维护的表,它指向每一个从堆上删除之前必须被终结的对象。

当垃圾回收器确定到了从内在中释放一个对象的时间时,它检查终结队列上的每一个项,并将对象从堆上复制到另一称作终结可达表(finalization reachable table)的托管

结构上。此时,下一个垃圾回收时将产生另一个线程,为每一个在可达表中的对象调用Finalize()方法。

因此,为了真正终结一个对象,至少要进行两次垃圾回收。

 

总结:

Finalize()是由垃圾回收器自动调用的,所以其在调用时,不能确定托管对象是否还存在,即它和其它托管对象通信是不安全的。所以其不能和托管对象相交互。而且,它调用的

具体时间我们也无从知。

 

二.构建可处置对象

   public interface IDisposable
        {
            void Dispose();
        }

这个作用是假设当对象用户不再使用这个对象时,会在这个对象引用离开作用域之前手工调用Dispose()方法。

这里的Dispose()不只负责释放一个对象的非托管资源,还应该对任何它包含的可处置对象调用Dispose(),即其还可以用于释放托管资源。这一点和Finalize()非常不一样。

它和其它托管对象通信是安全的。原因很简单:垃圾回收器并不支持IDisposable接口,永远不会调用Dispose()。因此,当对象的用户调用这个方法时,对象仍在托管堆上,

并可以访问所有其他分配在堆上的对象。

补充一下:用using可以调用Dispose()方法。

 

 

最后,在一个对象的定义中可以包含上面两种清理资源的技术。但注意在Dispose()方法中加上GC.SuppressFinalize(this)。因为如果用户调用了Dispose()就不需要终结,

因此可以跳过终结。

 

最后的最后,再一次强调一下:

Finalize()方法,我们是无法知道具体调用时间的。而且它不能处理托管资源。

Dispose(),我们是手工调用,是知道具体方法。这样很利于立即释放那些紧急资源。比Finalize()时间上要好。而且其可以处理托管和非托管资源。

所以,只要可能的话,我们在设计类时避免提供Finalize()方法,因为其是很花费时间的。尽可能用Dispose()来解决清理问题。

posted @ 2012-04-30 21:39  rarry  阅读(486)  评论(0编辑  收藏  举报