c#非托管资源释放

前言

 c#一般使用托管内存,不用担心资源释放问题。但如果调用操作系统资源,比如文件、窗口的句柄,数据库及网络连接;或者PInvoke调用C++的库。此时,需要程序员手动对这些资源进行管理。其中IDisposable接口,终结器,可以帮助我们封装这一释放过程。

官方示例

   微软的官方文档如下。这个类看起来有点繁琐,主要是为了支持多种情况:1.程序员想要立马释放资源2.程序员不关心什么时候释放,只要最终释放了就行。

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.

public class DisposeExample
{
    // A base class that implements IDisposable.
    // By implementing IDisposable, you are announcing that
    // instances of this type allocate scarce resources.
    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.
        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.
        public void Dispose()
        {
            Dispose(disposing: true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SuppressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            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.
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            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# finalizer syntax for finalization code.
        // This finalizer will run only if the Dispose method
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide finalizer in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(disposing: false) is optimal in terms of
            // readability and maintainability.
            Dispose(disposing: false);
        }
    }
    public static void Main()
    {
        // Insert code here to create
        // and use the MyResource object.
    }
}

相关概念

1.普通C#对象的释放:
 普通的C#对象在不再被引用时,会由垃圾回收器自动进行内存释放。当对象不再被引用时,垃圾回收器会在适当的时机将其标记为可回收对象,并在之后的垃圾回收过程中释放其占用的内存空间。

2.什么是含有资源的托管对象:
 也就是该对象本身是c#托管的,但其中有一些非托管资源,比如网络连接、数据连接、内存。与普通对象相比,也就是实现了IDispose接口。

  1. C#的析构函数(终结器)的作用:
     在C#中,析构函数(Finalizer)通常用于释放非托管资源或执行一些清理操作。含有终结期的对象释放具体流程如下:1.当一个对象不再被引用时,垃圾回收器会将其标记为待清理状态。2.如果该对象有终结器(Finalizer),则会被加入到终结队列中等待执行终结器方法。3.终结器方法是一个特殊的析构函数,用于在对象被销毁前执行一些清理操作。4.最后才是内存的真正释放

4.IDisposable接口如何帮助我们释放
 IDisposable接口本身中的Dispose方法,是用来释放托管资源的,可以主动调用对象的Dispose方法释放。另外c#其实是Using语法糖也支持这一过程,比如:Using(FileStream fs = new FileStream())。这种写法,在超出作用域以后,会自动调用IDisposable接口的Dispose方法。

示例分析

 回到最初的两种情况

  1. 程序员想要立马释放资源
     程序员主动调用MyResource的Dispose方法,或者用Using语句。其中Dispose(true)方法真正去释放非托管资源,并告诉GC,不用在释放该对象的资源了。
  2. 程序员不关心什么时候释放
     终结器在不确定的时间,发现可以回收该对象,调用终结器方法,其中Dispose(false)去真正释放资源。
protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            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;
            }
        }

最后是分析Dispose(bool disposing)方法。

  1. 其参数disposing用来标志是否需要释放其他托管资源(也就是含有资源的托管对象),对象中的disposed是用来标志是否释放过这个对象。
  2. disposing的if语句中释放的并不是普通的托管对象,比如string,List,int[]。普通的托管对象,不需要手动释放,GC会进行内存释放。if语句外释放的是本对象的资源。
    3.protect virtual,则子类可以继承该方法,并释放自己的资源。

总结

 这种写法很优雅,支持主动、被动,减少内存泄露

posted @   chendaxian  阅读(226)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示