Dispose and Finalizer in C#

CLR提供了自动内存管理。Managed memory不需要我们显式地释放。当进行Garbage Collection时,会自动释放。

但是,managed memory仅仅是许多种系统资源中的一种。除了managed memory之外的其他需要显式地释放的资源,被称为unmanaged resources,比如打开的文件描述符,打开的数据库连接等。

CLR提供了释放unmanaged resources的机制。System.Object中声明了一个virtual 方法 Finalize,该方法类似C++中的析构函数,当一个object的内存被回收时,GC会调用Finalize方法释放unmanaged resources。重写了Finalizer的类型也叫做Finalizable类型。

public class ComplexResourceHolder : IDisposable {
 
    private IntPtr buffer; // unmanaged memory buffer
    private SafeHandle resource; // disposable handle to a resource
        
    public ComplexResourceHolder(){
        this.buffer = ... // allocates memory
        this.resource = ... // allocates the resource
    }

    ~ ComplexResourceHolder(){
        ReleaseBuffer(buffer); // release unmanaged memory
    }
}

但是使用finalizer有不好的地方

  1. finalizer的调用时间是不确定的。我们不能主动地调用finalizer,只能当GC时由framework调用。这样对于一些稀缺的系统资源,这是不可接受的。
  2. GC准备回收一个对象的内存时,如果该对象需要finalize,那么会把该对象放到一个finalize队列中,然后另外一个线程会从该队列中取出对象并调用finalizer。这样,这个对象的内存最快要到下次GC(也有可能第三次、第四次)时才能被回收。因此可能会降低性能。

.Net Framework提供了System.IDisposable接口,通过实现该接口的Dispose方法,我们可以手动调用该方法,这样就可以自己控制unmanaged resources的释放时间。Framework也提供了GC.SuppressFinalize方法告诉GC该对象已经被手动disposed,不需要finalized。这样,该对象的内存就可以被尽快地回收。

.Net推荐的IDisposable接口实现方式如下

    public class ComplexResourceHolder : IDisposable {
     
        private IntPtr buffer; // unmanaged memory buffer
        private SafeHandle resource; // disposable handle to a resource
            
        public ComplexResourceHolder(){
            this.buffer = ... // allocates memory
            this.resource = ... // allocates the resource
        }
        
        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose(){
            Dispose(true);
            
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }
        
        // Use C# destructor syntax for finalization code.
        // This destructor will run only if the Dispose method
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide destructors in types derived from this class.
        ~ComplexResourceHolder(){
            Dispose(false);
        }
    
        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user‘s code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be disposed.
        protected virtual void Dispose(bool disposing){        
            
            // release unmanaged memory
            ReleaseBuffer(buffer); 
            
            // release other disposable objects
            if (disposing){
                if (resource!= null) 
                    resource.Dispose();
            }
        }    
    }

 

Reference:

1. http://msdn.microsoft.com/zh-cn/library/system.idisposable.dispose(v=vs.110).aspx

2. http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx

Dispose and Finalizer in C#

上一篇:C# 以管理员身份运行程序


下一篇:C# 委托应用总结