本文是翻译Memory Management and Garbage Collection in .NET,本人英语水平不行,语文水平也不行,若有错误恳请评论指正。本文权当是英语翻译练习。
文档的本部分提供了关于.NET中内存管理的信息。
非托管资源清除
描述如何合适的管理和清除非托管资源。
你的应用程序创建的绝大多数对象,你都可以依靠.NET的垃圾回收来处理内存管理。然而,当你创建的对象包括非托管资源时,必须在你的应用程序中完成使用这些资源后明确的释放这些资源。最常见的的非托管资源类型是封装操作系统资源的对象,例如文件、窗口、网络连接或者数据库连接。尽管垃圾回收有能力跟踪封装的非托管资源的对象的生命周期,但它不知道如何释放和清除这些非托管资源。
如果你定义的类型使用了非托管资源,你应该依照下面的方式去做(你应该执行以下操作):
- 实现Dispose模式。这要求你提供一个IDisposable.Dispose的实现来启用确定性的释放非托管资源。当你的类型使用者不再需要此对象时调用Dispose方法会立即释放非托管资源。
- 你的类型使用者忘了调用Dispose时,可以提供有两种方法一下两种方法以供释放非托管资源
- 使用安全句柄封装你的非托管资源。这是推荐的技术。安全句柄派生自System.Runtime.InteropServices.SafeHandle类并且包含一个健壮的Finalize方法。当你使用安全句柄时,你只需要实现IDisposable接口并在你实现的IDisposable.Dsipose中调用安全句柄的Dispose方法。如果它的Dispose方法没有被调用,安全句柄的终止器会被GC自动的调用。
- 重写Object.Finalize方法。当类型的使用者调用IDisposable.Dispose去明确的销毁它失败时,Finalization可以非明确的释放非托管资源。然而,对象finalization可能是复杂且易出错的操作,我们推荐你使用安全句柄替代你自己提供的finalizer。
你类型的使用者可以直接调用你实现的IDisposable.Dispose立即去释放被非托管资源使用的内存。当你适当的实现一个Dispose方法,当发生Dispose方法没有被调用时,不管是你的安全句柄Finalize方法还是重写的Object.Finalize方法都成为清除资源的保障。
以下代码是实现IDisposable接口的范例:
using System;
using System.ComponentModel;
// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.
// 以下例子示范如何创建一个实现IDisposable接口和IDisposable.Dispose方法的封装了非托管资源的类
public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
// 一个基本的实现了IDispose接口类
// 通过实现IDisposable接口,你可以声明此类型的实例中分配稀缺资源(不好翻译,大意应该是可以在此类型的实例分配非托管资源)
public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.
// 指向外部非托管资源的指针
private IntPtr handle;
// Other managed resource this class uses.
// 此类使用的其他托管资源
private Component component = new Component();
// Track whether Dispose has been called.
// 跟踪Dispose方法是否被调用
private bool disposed = false;
// The class constructor.
public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
// 实现IDispoable接口,不要将此方法声明为虚方法,派生类不可以重写此方法
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.
// 当前对象不能被Dispose方法清除,因此你需要调用GC.SupressFinalize将当前对象从finalization(终结器)队列中移除,防止finalization为当前对象在执行列表中指定两次
//(Dispose方法会将当前对象加入Finalization队列,析构函数(Object.Finalize)又会将当前对象添加到Finalization队列,为了防止此现象发生,需要在Dispose中调用GC.SupressFinalize将其移除)
GC.SuppressFinalize(this);
}
// 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.
// disposing为true时,此方法直接或间接的被用户代码调用,托管和非托管资源都会被释放
// disposing为false时,此方法被从内置的finalizer运行时调用,你不可以影响到其他对象(不可以释放托管资源,运行时会自动释放),只有非托管对象被释放。
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
// 确认Dispose是否已经被调用
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
// 释放指定的托管资源
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note disposing has been done.
disposed = true;
}
}
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// 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.
// 使用c#析构语句为finalization代码
// 只有在Dispose没有被调用时才会运行此析构函数
// 它为你的基类提供了finalize的机会
// 不要在此类型的派生类中提供析构函数
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
}
非托管资源如何释放?
- 实现IDisposable接口,使用者在使用完后调用Dispose方法释放。
- 继承安全句柄类System.Runtime.InteropServices.SafeHandle,此类包含一个健壮的Finalize方法。若Dispose未被使用者调用,安全句柄的Finalizer会被GC自动调用。这是推荐的方法。
- 重写Object.Finalize方法(析构函数编译成IL自动生成),这是不被推荐的方法,因为不知道什么时候被运行时调用,而且可能出现异常。
使用Dispose释放非托管资源被称为Dispose Pattern(Dispose 模式),dispose模式有两个变种(普通的就是单纯实现IDisposable接口的Dispose方法再加一个protected virtual void Dispose(bool disposing)方法)
- 实现IDisposable接口并重写Object.Finalize方法(写析构函数)
- 继承安全句柄类System.Runtime.InteropServices.SafeHandle,安全句柄类实现了IDisposable接口,并重写了Object.Finalize方法(~SafeHandle)
综述:非托管资源在使用完成一定要释放,可以采用普通dispose模式,最有保障的方法是继承安全句柄类,重写Object.Finalize方法也可以但不被推荐。
tips:
- GC.KeepAlive(object obj):延长对象的生命周期,防止引用计数为0的对象被回收。局部变量作为参数传递给非托管代码时特别要注意,可能会出现无法预料的错误。
Dispose的目的是释放非托管资源!!!
如何使用实现了IDispose接口的对象
- 使用using语句
- try/finally块
以下为Dispose及dispose模式的实现代码范例:
标准IDisposable.Dispose实现:
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
// 默认Dispose方法会清理所有对象(包括托管),所以GC不再需要调用对象重写的Finalize(析构函数)。因此调用GC.SuppressFinalize可以防止GC调用Finalize(防止调用两次析构函数)。
GC.SuppressFinalize(this);
}
一般的dispose模式实现:
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new Microsoft.Win32.SafeHandles.SafeFileHandle(IntPtr.Zero, true);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
}
}
重写Object.Finalize 的一般dispose模式
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new Microsoft.Win32.SafeHandles.SafeFileHandle(IntPtr.Zero, true);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
// Dispose of unmanaged resources.
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
}
~BaseClass()
{
Dispose(false);
}
}
派生类实现dispose模式
class DerivedClass : BaseClass
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
// Free any other managed objects here.
//
}
// Free any unmanaged objects here.
//
disposed = true;
// Call base class implementation.
base.Dispose(disposing);
}
~DerivedClass()
{
Dispose(false);
}
}
派生自安全句柄类实现dispose模式
public class DisposableStreamResource : IDisposable
{
// Define constants.
protected const uint GENERIC_READ = 0x80000000;
protected const uint FILE_SHARE_READ = 0x00000001;
protected const uint OPEN_EXISTING = 3;
protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
protected IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);
// Define Windows APIs.
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
protected static extern IntPtr CreateFile(
string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll")]
private static extern int GetFileSize(SafeFileHandle hFile, out int lpFileSizeHigh);
// Define locals.
private bool disposed = false;
private SafeFileHandle safeHandle;
private long bufferSize;
private int upperWord;
public DisposableStreamResource(string filename)
{
if (filename == null)
throw new ArgumentNullException("The filename cannot be null.");
else if (filename == "")
throw new ArgumentException("The filename cannot be an empty string.");
IntPtr handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero);
if (handle != INVALID_HANDLE_VALUE)
safeHandle = new SafeFileHandle(handle, true);
else
throw new FileNotFoundException(String.Format("Cannot open '{0}'", filename));
// Get file size.
bufferSize = GetFileSize(safeHandle, out upperWord);
if (bufferSize == INVALID_FILE_SIZE)
bufferSize = -1;
else if (upperWord > 0)
bufferSize = (((long)upperWord) << 32) + bufferSize;
}
public long Size
{ get { return bufferSize; } }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
// Dispose of managed resources here.
if (disposing)
safeHandle.Dispose();
// Dispose of any unmanaged resources not wrapped in safe handles.
disposed = true;
}
}
派生自安全句柄的子类实现:
public class DisposableStreamResource2 : DisposableStreamResource
{
// Define additional constants.
protected const uint GENERIC_WRITE = 0x40000000;
protected const uint OPEN_ALWAYS = 4;
// Define additional APIs.
[DllImport("kernel32.dll")]
protected static extern bool WriteFile(
SafeFileHandle safeHandle, string lpBuffer,
int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten,
IntPtr lpOverlapped);
// Define locals.
private bool disposed = false;
private string filename;
private bool created = false;
private SafeFileHandle safeHandle;
public DisposableStreamResource2(string filename) : base(filename)
{
this.filename = filename;
}
public void WriteFileInfo()
{
if (!created)
{
IntPtr hFile = CreateFile(@".\FileInfo.txt", GENERIC_WRITE, 0,
IntPtr.Zero, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (hFile != INVALID_HANDLE_VALUE)
safeHandle = new SafeFileHandle(hFile, true);
else
throw new IOException("Unable to create output file.");
created = true;
}
string output = String.Format("{0}: {1:N0} bytes\n", filename, Size);
int bytesWritten;
bool result = WriteFile(safeHandle, output, output.Length, out bytesWritten, IntPtr.Zero);
}
protected new virtual void Dispose(bool disposing)
{
if (disposed) return;
// Release any managed resources here.
if (disposing)
safeHandle.Dispose();
disposed = true;
// Release any unmanaged resources not wrapped by safe handles here.
// Call the base class implementation.
base.Dispose(true);
}
}