DotNet · 2019年11月13日

.NET托管资源与非托管资源介绍

在.NET中,每一种类型都代表一种资源,而资源分为托管资源与非托管资源两类:

1) 托管资源:由CLR分配和释放的资源,即从CLR中new声明的对象,一般指托管内存。托管资源进一步又可分为:

  a、普通类型托管资源:如int、String、float等;

  b、非普通类型托管资源:继承了IDispose接口的类实例化后对应的对象,此类资源被GC回收时需经2个步骤,会降低系统性能,因此在实现Dispose方法时需手动清理;

2) 非托管资源:不受CLR管理的对象,如Windows内核对象、文件、数据库连接、套接字、COM对象等;

注:1) 继承IDispose接口的类实例化的对象不是非托管资源,个人理解:非托管资源几乎无法在程序中自定义。

      2) 个人理解,数据库连接如SqlConnection本身是托管资源,只是内部包含了非托管资源-数据库连接的系统资源。

——————————————————–

  如果我们定义的类型使用到了非托管资源或非普通类型托管资源,就需要显示地释放托管资源,实现方法是让类型继承IDispose接口,并实现接口中的Dispose方法。标准的Dispose模式实现方式如下:

复制代码
//演示需要,自定义非普通托管类型
class MyResource : IDisposable
{
    public void Dispose(){ }
}

public class SampleClass : IDisposable
{
    //演示创建一个非托管资源
    private IntPtr nativeResource = Marshal.AllocHGlobal(100);
    //演示创建一个非普通类型托管资源,MyResource实现了接口IDisposable,是一个非普通类型的托管资源
    private MyResource myResource = new MyResource();
    //演示创建一个普通类型的托管资源
    private String name = "hduhans";

    private bool disposed = false;

    /// <summary>
    /// 实现IDisposable中的Dispose方法
    /// </summary>
    public void Dispose()
    {
        //必须为true,显示释放所有资源
        Dispose(true);
        //通知垃圾回收机制不再调用终结器(析构器),也就是GC不会调用~SampleClass()方法
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 必须,防止程序员忘记了显式调用Dispose方法,这时可由GC来回收并释放非托管资源
    /// 注意在析构函数中不允许释放托管资源,因为GC在调用析构函数前可能已经释放了托管资源,此处再释放可能会引发异常
    /// </summary>
    ~SampleClass()
    {
        //必须为false
        Dispose(false);
    }

    /// <summary>
    /// 如果子类继承当前类型并实现了自己的Dispose方法,必须调用父类的Dispose方法来释放父类中的资源,调用:base.Dispose(disposing)
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
        {
            return;
        }
        if (disposing)
        {
            // 清理非普通类型(继承IDispose接口)的托管资源,普通类型的托管资源(此例中name)不需要手动清理,GC会自动清理
            if (myResource != null)
            {
                myResource.Dispose();
                myResource = null;
            }
        }
        // 清理非托管资源
        if (nativeResource != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(nativeResource);
            nativeResource = IntPtr.Zero;
        }
        //让类型知道自己已经被释放
        disposed = true;
    }
}
复制代码

     此例中需要注意的是:

   1) 即使提供了显示释放的方法Dispose,也应该在终结器中提供隐式清理操作,即在终结器(析构函数)中释放非托管资源。如果程序员忘了调用Dispose方法,则只能靠终结器来释放非托管资源,否则非托管资源无法释放,从而造成内存泄漏。此例:~SampleClass(){ Dispose(false); }  

     2) 在Dispose模式中应提供受保护的虚方法,如果子类继承当前类型并实现了自己的Dispose模式,受保护的虚方法用来提醒子类:必须在实现自己清理方法时注意到父类的清理工作,即子类需调用base.Dispose()。此例:protected virtual void Dispose(bool disposing){ //… } 

——————————————————–

   对于继承IDispose接口的对象,可用using语法糖:

using (SampleClass samleClass = new SampleClass()) {
         //操作...
}

  等效于:

复制代码
SampleClass samleClass = null;
try {
    samleClass = new SampleClass()
    //操作...
} finally { 
    samleClass.Dispose();
}

最新电影,电视剧,尽在午夜剧场

电影电视剧午夜不寂寞