.NET框架自动内存管理(5)
5.清理非托管资源的Dispose模式
当托管对象封装了非托管资源时,为保证垃圾回收器能回收非托管资源,必须重写Finalize方法,在Finalize方法中封装能够清理非托管资源的代码。有时希望消耗大量内存的非托管资源能够在垃圾回收周期到来之前被回收以提高应用程序的性能,这时就需要手动清理非托管资源。所幸的是,.NET框架类库(FCL)提供了实现手动清理非托管资源的基本框架,这就是Dispose模式。
IDisposable接口是实现Dispose模式的核心。IDisposable接口定义如下:
public interface IDisposable
{
void Dispose();
}
在Dispose模式中,所有封装非托管资源的对象都必须实现IDisposable接口,并在Dispose方法中实现非托管资源的清理操作。这样应用程序可以通过IDisposable接口直接调用Dispose方法来释放非托管资源占用的内存,而Finalize方法则成为在没有调用Dispose方法的情况下由垃圾回收器自动启动非托管资源清理的一种防护措施。为保证由应用程序直接调用Dispose方法进行的非托管资源清理操作与垃圾回收器自动启动的非托管资源清理操作过程是一致的,在Dispose模式中,通常在析构函数(也就是重写的Finalize方法)中调用Dispose方法。
使用Dispose模式需要遵循以下原则:
1. 如果基类实现了IDisposable接口,则在派生类的Dispose方法实现中必须调用基类的Dispose方法,以保证封装在基类对象中的非托管资源能够被清理;
2. 对象必须通过在包容层次结构中传播Dispose方法的调用以确保及时释放所有保持的非托管资源。例如,如果对象A分配对象B,则A的Dispose方法实现必须调用B的Dispose方法;
3. 必须确保对象的Dispose方法只被实质调用一次,而且不得引发异常,因为异常将阻断Dispose的调用链。如果对象的Dispose方法被调用多次,必须忽略第一次调用以后的所有调用,否则当非托管资源已释放后再次调用Dispose方法必然发生资源释放错误而不得不引发异常;
4. 由于调用Finalize方法将大大减损性能,所以如果应用程序已显式调用Dispose方法完成了资源清理工作,垃圾回收器就没必要调用对象的Finalize方法了。这时应该在Dispose方法中调用GC.SuppressFinalize方法,以阻止其Finalize方法被垃圾回收器调用,从而提高应用程序性能;
5. 如果资源使用特定的约定表示其已分配状态和已释放状态,例如流或数据库连接,则封装该类资源的对象通常还应提供分配资源的Open方法和释放资源的Close方法。在Close方法中调用Dispose方法;
6. 作为资源释放的最后一道防护措施,实现IDisposable接口的对象通常应重写Finalize方法。
下面的代码示例阐释了Dispose模式的实现原则及实现框架。
/// <summary>
/// This class uses the dispose pattern to provide a skeleton for resource cleanup operations.
/// </summary>
/// <remarks>
/// The class wraps unmanaged resources can be derived from this type.
/// It is the derived class's responsibility to override the protected virtual
/// <see cref="Dispose(bool)"/> method for doing actual cleanup operation.
/// </remarks>
public abstract class DisposeBase : System.IDisposable
{
#region implements IDisposable interface
/// <summary>
/// This class uses the dispose pattern to provide a skeleton for resource cleanup operations.
/// </summary>
/// <remarks>
/// The class wraps unmanaged resources can be derived from this type.
/// It is the derived class's responsibility to override the protected virtual
/// <see cref="Dispose(bool)"/> method for doing actual cleanup operation.
/// </remarks>
public abstract class DisposeBase : System.IDisposable
{
#region implements IDisposable interface
/// <summary>
/// This class uses the dispose pattern to provide a skeleton for resource cleanup operations.
/// </summary>
/// <remarks>
/// The class wraps unmanaged resources can be derived from this type.
/// It is the derived class's responsibility to override the protected virtual
/// <see cref="Dispose(bool)"/> method for doing actual cleanup operation.
/// </remarks>
public abstract class DisposeBase : System.IDisposable
{
#region implements IDisposable interface
/// <summary>
/// This public method can be called to deterministically release all resources,
/// including both managed and unmanaged resources, used by the derived classes.
/// </summary>
/// <remarks>
/// This is not a virtual method
/// as most classes implementing <see cref="IDisposable"/> interface in FCL make
/// this method virtual. The derived classes should not override this method.
/// </remarks>
public void Dispose()
{
// because this object will be explicitly cleaned up, stop the garbage collector
// from calling the Finalize method.
GC.SuppressFinalize(this);
/// <summary>
/// This public method can be called to deterministically release all resources,
/// including both managed and unmanaged resources, used by the derived classes.
/// </summary>
/// <remarks>
/// This is not a virtual method
/// as most classes implementing <see cref="IDisposable"/> interface in FCL make
/// this method virtual. The derived classes should not override this method.
/// </remarks>
public void Dispose()
{
// because this object will be explicitly cleaned up, stop the garbage collector
// from calling the Finalize method.
GC.SuppressFinalize(this);
/// <summary>
/// This public method can be called to deterministically release all resources,
/// including both managed and unmanaged resources, used by the derived classes.
/// </summary>
/// <remarks>
/// This is not a virtual method
/// as most classes implementing <see cref="IDisposable"/> interface in FCL make
/// this method virtual. The derived classes should not override this method.
/// </remarks>
public void Dispose()
{
// because this object will be explicitly cleaned up, stop the garbage collector
// from calling the Finalize method.
GC.SuppressFinalize(this);
// call the method that actually does the cleanup.
Dispose(true);
}
// call the method that actually does the cleanup.
Dispose(true);
}
// call the method that actually does the cleanup.
Dispose(true);
}
#endregion
#endregion
#endregion
#region virtual Dispose method
#region virtual Dispose method
#region virtual Dispose method
/// <summary>
/// This method is designed to be overridden by the derived classes to do
/// the actual clean up operations.
/// </summary>
/// <param name="disposing">
/// A <see langword="bool" /> value to indicate whether this method is called
/// explicitly by the application or implicitly by the finalizer.
/// This method executes in two distinct scenarios.
/// <para>
/// If <paramref name="disposing" /> is <see langword="true" />,
/// the method should has been called directly or indirectly by the application
/// from inside the public <see cref="Dispose()"/> method.
/// Managed and unmanaged can be disposed.
/// </para>
/// <para>
/// If <paramref name="disposing" /> is <see langword="false" />,
/// the method should has been called by the garbage collector from inside the
/// <see cref="Finalize"/> method.
/// Only unmanaged can be disposed.
/// </para>
/// </param>
/// <remarks>
/// The derived classes should override this virtual method to do
/// the actual clean up operations. and call the base class's virtual
/// <b>Dispose(Boolean)</b> method within the overridden method.
/// </remarks>
protected virtual void Dispose(bool disposing)
{
// does nothing in the base class.
}
/// <summary>
/// This method is designed to be overridden by the derived classes to do
/// the actual clean up operations.
/// </summary>
/// <param name="disposing">
/// A <see langword="bool" /> value to indicate whether this method is called
/// explicitly by the application or implicitly by the finalizer.
/// This method executes in two distinct scenarios.
/// <para>
/// If <paramref name="disposing" /> is <see langword="true" />,
/// the method should has been called directly or indirectly by the application
/// from inside the public <see cref="Dispose()"/> method.
/// Managed and unmanaged can be disposed.
/// </para>
/// <para>
/// If <paramref name="disposing" /> is <see langword="false" />,
/// the method should has been called by the garbage collector from inside the
/// <see cref="Finalize"/> method.
/// Only unmanaged can be disposed.
/// </para>
/// </param>
/// <remarks>
/// The derived classes should override this virtual method to do
/// the actual clean up operations. and call the base class's virtual
/// <b>Dispose(Boolean)</b> method within the overridden method.
/// </remarks>
protected virtual void Dispose(bool disposing)
{
// does nothing in the base class.
}
/// <summary>
/// This method is designed to be overridden by the derived classes to do
/// the actual clean up operations.
/// </summary>
/// <param name="disposing">
/// A <see langword="bool" /> value to indicate whether this method is called
/// explicitly by the application or implicitly by the finalizer.
/// This method executes in two distinct scenarios.
/// <para>
/// If <paramref name="disposing" /> is <see langword="true" />,
/// the method should has been called directly or indirectly by the application
/// from inside the public <see cref="Dispose()"/> method.
/// Managed and unmanaged can be disposed.
/// </para>
/// <para>
/// If <paramref name="disposing" /> is <see langword="false" />,
/// the method should has been called by the garbage collector from inside the
/// <see cref="Finalize"/> method.
/// Only unmanaged can be disposed.
/// </para>
/// </param>
/// <remarks>
/// The derived classes should override this virtual method to do
/// the actual clean up operations. and call the base class's virtual
/// <b>Dispose(Boolean)</b> method within the overridden method.
/// </remarks>
protected virtual void Dispose(bool disposing)
{
// does nothing in the base class.
}
#endregion
#endregion
#endregion
#region finalizer
#region finalizer
#region finalizer
/// <summary>
/// This <b>Finalize</b> method is called by garbage collection to release
/// unmanaged resources and perform other clean up operations. It is run
/// only if the public parameterless <see cref="Dispose()"/> method does not get
/// called directly by the application.
/// <para>In C#, finalizers are expressed using destructor syntax.</para>
/// </summary>
/// <remarks>Do not override Finalize method in types derived from this class.
/// The virtual <see cref="Dispose(bool)"/> method with a <see langword="bool" />
/// parameter is interally called to do the actual clean up operations.
/// </remarks>
~DisposeBase()
{
// call the method that actually does the cleanup.
// calling Dispose(false) is optimal in terms of readability and maintainability.
Dispose(false);
}
/// <summary>
/// This <b>Finalize</b> method is called by garbage collection to release
/// unmanaged resources and perform other clean up operations. It is run
/// only if the public parameterless <see cref="Dispose()"/> method does not get
/// called directly by the application.
/// <para>In C#, finalizers are expressed using destructor syntax.</para>
/// </summary>
/// <remarks>Do not override Finalize method in types derived from this class.
/// The virtual <see cref="Dispose(bool)"/> method with a <see langword="bool" />
/// parameter is interally called to do the actual clean up operations.
/// </remarks>
~DisposeBase()
{
// call the method that actually does the cleanup.
// calling Dispose(false) is optimal in terms of readability and maintainability.
Dispose(false);
}
/// <summary>
/// This <b>Finalize</b> method is called by garbage collection to release
/// unmanaged resources and perform other clean up operations. It is run
/// only if the public parameterless <see cref="Dispose()"/> method does not get
/// called directly by the application.
/// <para>In C#, finalizers are expressed using destructor syntax.</para>
/// </summary>
/// <remarks>Do not override Finalize method in types derived from this class.
/// The virtual <see cref="Dispose(bool)"/> method with a <see langword="bool" />
/// parameter is interally called to do the actual clean up operations.
/// </remarks>
~DisposeBase()
{
// call the method that actually does the cleanup.
// calling Dispose(false) is optimal in terms of readability and maintainability.
Dispose(false);
}
#endregion
}
#endregion
}
#endregion
}
// dispose pattern for a derived class.
// This class internally implements the IDisposable interface and impose
// dispose pattern on resource cleanup.
public class ResourceWrapper : DisposeBase
{
// unmanaged resource pointer
private IntPtr handle;
// dispose pattern for a derived class.
// This class internally implements the IDisposable interface and impose
// dispose pattern on resource cleanup.
public class ResourceWrapper : DisposeBase
{
// unmanaged resource pointer
private IntPtr handle;
// dispose pattern for a derived class.
// This class internally implements the IDisposable interface and impose
// dispose pattern on resource cleanup.
public class ResourceWrapper : DisposeBase
{
// unmanaged resource pointer
private IntPtr handle;
// managed resource reference
private Component component;
// managed resource reference
private Component component;
// managed resource reference
private Component component;
// indicator to track whether Dispose has been called
private bool disposed = false;
// indicator to track whether Dispose has been called
private bool disposed = false;
// indicator to track whether Dispose has been called
private bool disposed = false;
// A more meaningful method to release resources.
public void Close()
{
Dispose();
}
// A more meaningful method to release resources.
public void Close()
{
Dispose();
}
// A more meaningful method to release resources.
public void Close()
{
Dispose();
}
// override the virtual method to do the actual cleanup.
protected override void Dispose(bool disposing)
{
// synchronize threads calling Dispose()/Close() simultaneously
lock(this)
{
// dispose operation should not be done more than once
if (!this.disposed)
{
try
{
// if disposing is true, release all the managed and
// unmanaged resources.
// this is the case when the Dispose(bool) is called by
// Dispose()/Close().
if (disposing)
{
// release managed resources
component.Dispose();
}
// if disposing is false, only unmanaged resources are released.
// this is the case when the Dispose(bool) is called by finalizer.
// release unmanaged resources
if (IntPtr.Zero != handle)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
// override the virtual method to do the actual cleanup.
protected override void Dispose(bool disposing)
{
// synchronize threads calling Dispose()/Close() simultaneously
lock(this)
{
// dispose operation should not be done more than once
if (!this.disposed)
{
try
{
// if disposing is true, release all the managed and
// unmanaged resources.
// this is the case when the Dispose(bool) is called by
// Dispose()/Close().
if (disposing)
{
// release managed resources
component.Dispose();
}
// if disposing is false, only unmanaged resources are released.
// this is the case when the Dispose(bool) is called by finalizer.
// release unmanaged resources
if (IntPtr.Zero != handle)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
// override the virtual method to do the actual cleanup.
protected override void Dispose(bool disposing)
{
// synchronize threads calling Dispose()/Close() simultaneously
lock(this)
{
// dispose operation should not be done more than once
if (!this.disposed)
{
try
{
// if disposing is true, release all the managed and
// unmanaged resources.
// this is the case when the Dispose(bool) is called by
// Dispose()/Close().
if (disposing)
{
// release managed resources
component.Dispose();
}
// if disposing is false, only unmanaged resources are released.
// this is the case when the Dispose(bool) is called by finalizer.
// release unmanaged resources
if (IntPtr.Zero != handle)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
// resources have been released successfully.
this.disposed = true;
}
finally
{
// always calls the base class's Dispose(bool) to let it have
// the opportunity to do its cleanup.
base.Dispose(disposing);
}
}
}
}
// resources have been released successfully.
this.disposed = true;
}
finally
{
// always calls the base class's Dispose(bool) to let it have
// the opportunity to do its cleanup.
base.Dispose(disposing);
}
}
}
}
// resources have been released successfully.
this.disposed = true;
}
finally
{
// always calls the base class's Dispose(bool) to let it have
// the opportunity to do its cleanup.
base.Dispose(disposing);
}
}
}
}
// Private method called to free the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
}
// Private method called to free the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
}
// Private method called to free the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
}