• C# 托管资源与非托管资源(参考二)


    本文转自:https://www.cnblogs.com/yubinfeng/p/4625833.html

    本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收。掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常。

    1.什么是托管与非托管?

    托管资源:一般是指被CLR(公共语言运行时)控制的内存资源,这些资源由CLR来管理。可以认为是.net 类库中的资源。

    非托管资源:不受CLR控制和管理的资源。

    对于托管资源,GC负责垃圾回收。对于非托管资源,GC可以跟踪非托管资源的生存期,但是不知道如何释放它,这时候就要人工进行释放。

    2.哪些资源是非托管的?

    总体来说就是 不受CLR控制和管理的资源

    包括:比如文件流、图像图形类、数据库的连接,网络连接,系统的窗口句柄,打印机资源等,这类资源一般不存在堆上。可以认为操作系统资源的一组API。

    原则:如果我们的类使用的非托管资源,如数据库连接、文件句柄,这些资源需做到:使用后立刻释放。

    3.如何释放非托管资源?

    .NET对于释放资源,标准做法如下:

    (1)继承IDisposable接口;

    (2)实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);

    (3) 实现类析构函数,在其中释放非托管资源。

     对于Dispose()方法的几个参数说明:

    A. 参数为true表示释放所有资源,只能由使用者调用

     参数为false表示释放非托管资源,只能由垃圾回收器自动调用

    C. 如果子类有自己的非托管资源,可以重载这个函数,添加自己的非托管资源的释放

    D.但是要记住,重载此函数必须保证调用基类的版本,以保证基类的资源正常释放

    4.举例说明资源释放

    4.1 数据库连接释放

    (1)错误做法:

       SqlConnection conn = new SqlConnection();

        //do something;

    conn.Dispose();

    此段代码,如果出现异常conn.Dispose()未能及时执行,会导致没有及时关闭连接。

    (2)正确做法:       

    复制代码
    SqlConnection conn = new SqlConnection();
    try
    {
        //do something;
    }
    finally
    {
        conn.Dispose();
    }
    复制代码

      对于以上代码,我们还可以简化代码量,c#使用using简化输入,编译器自动翻译成 try...finally,改为下面写法

    (3)改进做法: 

    using(SqlConnection conn = new SqlConnection())
    {
          //do something;
    }

    4.2 资源回收综合示例 

    复制代码
    public class BaseResource : IDisposable
        {
            private IntPtr handle; // 句柄,属于非托管资源
            private System.ComponentModel.Component comp; // 组件,托管资源
            private bool isDisposed = false; // 是否已释放资源的标志
    
            //实现接口方法
            //由类的使用者,在外部显示调用,释放类资源
            public void Dispose()
            {
                Dispose(true);// 释放托管和非托管资源
    
                //将对象从垃圾回收器链表中移除,
                // 从而在垃圾回收器工作时,只释放托管资源,而不执行此对象的析构函数
                GC.SuppressFinalize(this);
            }
    
            //由垃圾回收器调用,释放非托管资源
            ~BaseResource()
            {
                Dispose(false);// 释放非托管资源
            }
    
            //参数为true表示释放所有资源,只能由使用者调用
            //参数为false表示释放非托管资源,只能由垃圾回收器自动调用
            //如果子类有自己的非托管资源,可以重载这个函数,添加自己的非托管资源的释放
            //但是要记住,重载此函数必须保证调用基类的版本,以保证基类的资源正常释放
            protected virtual void Dispose(bool disposing)
            {
                if (!this.isDisposed)// 如果资源未释放 这个判断主要用了防止对象被多次释放
                {
                    if (disposing)
                    {
                        comp.Dispose();// 释放托管资源
                    }               
                }
                this.isDisposed = true; // 标识此对象已释放
            }
        }
    复制代码

    5.对于释放资源注意的几点

    A. 显示调用Dispose()方法,可以及时的释放资源,同时通过移除Finalize()方法的执行,提高了性能;

    B. 如果没有显式调用Dispose()方法,垃圾回收器也可以通过析构函数来释放非托管资源,垃圾回收器本身就具有回收托管资源的功能,从而保证资源的正常释放,只不过由垃圾回收器回收会导致非托管资源的未及时释放的浪费。

    C. 在.NET中应该尽可能的少用析构函数释放资源。在没有析构函数的对象在垃圾处理器一次处理中从内存删除,但有析构函数的对象,需要两次,第一次调用析构函数,第二次删除对象。而且在析构函数中包含大量的释放资源代码,会降低垃圾回收器的工作效率,影响性能。

    D. 对于包含非托管资源的对象,最好及时的调用Dispose()方法来回收资源,而不是依赖垃圾回收器

    E. 析构函数只能由垃圾回收器调用。

    F. Despose()方法只能由类的使用者调用。

    G. 在.NET中,凡是继承了IDisposable接口的类,都可以使用using语句,从而在超出作用域后,让系统自动调用Dispose()方法。
    H. 一个资源安全的类,都实现了IDisposable接口和析构函数。提供手动释放资源和系统自动释放资源的双保险。

    6. 关于Finalize和Dispose

    (1)、Finalize只释放非托管资源;

    (2)、Dispose释放托管和非托管资源;

    (3)、重复调用Finalize和Dispose是没有问题的;

    (4)、Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。

    7. 要点:

    本节内容比较简单,主要了解了.NET托管和非托管两种资源,绝大多部资源由CLR(公共语言运行时)自动释放,称为托管代码;还有一部分资源需要人工释放,称为非托管代码。掌控非托管代码的几种内存释放方法,可以以效的避免程序产生异常,合理释放内存,有利于程序稳定和流畅。

  • 相关阅读:
    CodeForces
    [AHOI 2013] 差异
    BZOJ
    [校内训练20_09_15]ABC
    [校内训练20_09_10]ABC
    [校内训练20_09_08]AC
    fastIO
    [校内训练20_06_05]ABC
    [校内训练20_06_04]ABC
    [校内训练20_06_03]ABC
  • 原文地址:https://www.cnblogs.com/hanguoshun/p/12738130.html
Copyright © 2020-2023  润新知