一. 构建可终结对象
//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()来解决清理问题。