.Net内存回收相关:析构函数、Finalize

JerryXia 发表于 , 阅读 (2,019)

对于您的应用程序创建的大多数对象,可以依靠 .NET Framework的垃圾回收器隐式地执行所有必要的内存管理任务。但是,在您创建封装非托管资源的对象时,当您在应用程序中使用完这些非托管资源之后,您必须显式地释放它们。 最常见的一类非托管资源就是包装操作系统资源的对象,例如文件、窗口或网络连接。虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。对于这些类型的对象,.NET Framework提供Object.Finalize方法,它允许对象在垃圾回收器回收该对象使用的内存时适当清理其非托管资源。默认情况下,Finalize方法不执行任何操作。如果您要让垃圾回收器在回收对象的内存之前对对象执行清理操作,您必须在类中重写Finalize方法。

GC回收流程

垃圾回收器使用名为“终止队列”的内部结构跟踪具有 Finalize 方法的对象。 每次您的应用程序创建具有 Finalize 方法的对象时,垃圾回收器都在终止队列中放置一个指向该对象的项。 托管堆中所有需要在垃圾回收器回收其内存之前调用它们的终止代码的对象都在终止队列中含有项。

Finalize方法不应引发异常,因为应用程序无法处理这些异常,而且这些异常会导致应用程序终止。

实现Finalize方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用Finalize方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。 它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用Finalize方法,然后,将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。

.Net程序员在编程时应该怎么做,有没有一种既简单又有有效的方法来处理内在回收。我作以下建议,望各路高手不吝赐教:

  1. 对于不包涵或没有引用(直接或间接)非托管资源的类,特别是作用如同Struct的实体类,析构、终结器、Dispose均不采用。
  2. 对于包涵非托管资源的类,如数据库连接对象,文件句柄等,应继承IDispose接口,在Dispose方法中清理非托管对象。客户代码用using(…){}格式显示调用Dispose。如果继承了IDispose接口,Dispose方法就不要留空,这样没有任何意义。除了构造器,任何方法体留空都有害无益。
  3. 所有自定义类一般均不建议显式声明析构函数、Finalize方法。

添加新评论