.Net 内存管理和垃圾回收(一)非托管资源清除

本文是翻译Memory Management and Garbage Collection in .NET,本人英语水平不行,语文水平也不行,若有错误恳请评论指正。本文权当是英语翻译练习。


文档的本部分提供了关于.NET中内存管理的信息。

  1. .Net 内存管理和垃圾回收(一)非托管资源清除
  2. .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);
        }
    }
}

非托管资源如何释放?

  1. 实现IDisposable接口,使用者在使用完后调用Dispose方法释放。
  2. 继承安全句柄类System.Runtime.InteropServices.SafeHandle,此类包含一个健壮的Finalize方法。若Dispose未被使用者调用,安全句柄的Finalizer会被GC自动调用。这是推荐的方法。
  3. 重写Object.Finalize方法(析构函数编译成IL自动生成),这是不被推荐的方法,因为不知道什么时候被运行时调用,而且可能出现异常。

使用Dispose释放非托管资源被称为Dispose Pattern(Dispose 模式),dispose模式有两个变种(普通的就是单纯实现IDisposable接口的Dispose方法再加一个protected virtual void Dispose(bool disposing)方法)

  1. 实现IDisposable接口并重写Object.Finalize方法(写析构函数)
  2. 继承安全句柄类System.Runtime.InteropServices.SafeHandle,安全句柄类实现了IDisposable接口,并重写了Object.Finalize方法(~SafeHandle)

综述:非托管资源在使用完成一定要释放,可以采用普通dispose模式,最有保障的方法是继承安全句柄类,重写Object.Finalize方法也可以但不被推荐。


tips:

扫描二维码关注公众号,回复: 1079902 查看本文章
  • 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);
        }
    }

猜你喜欢

转载自blog.csdn.net/lwwl12/article/details/79917806