zoukankan      html  css  js  c++  java
  • 说说非托管资源的回收

    释放未托管的资源有两种方法

     

    1、析构函数

    2、实现System.IDisposable接口

     

    一、析构函数  

    构造函数可以指定必须在创建类的实例时进行的某些操作,在垃圾收集器删除对象时,也可以调用析构函数。析构函数初看起来似乎是放置释放未托管资源、执行一般清理操作的代码的最佳地方。但是,事情并不是如此简单。由于垃圾回收器的运行规则决定了,不能在析构函数中放置需要在某一时刻运行的代码,如果对象占用了宝贵而重要的资源,应尽可能快地释放这些资源,此时就不能等待垃圾收集器来释放了.  

    实例

    using System;
    
    using System.Collections.Generic;
    
    using System.Linq;
    
    using System.Text; 
    
    namespace MemRelease
    
    {
        class Program
        {
            ~Program()
            {
                // Orders.
            } 
    
            static void Main(string[] args)
            {
            }
        }
    } 

    在IL DASM中,你会发现并没有这个析构的方法。C#编译器在编译析构函数时,会隐式地把析构函数的代码编译为Finalize()方法的对应代码,确保执行父类的Finalize()方法 看下这段代码中对于析构函数的编译:

    .method family hidebysig virtual instance void 
    
            Finalize() cil managed
    
    {
      // Code size       14 (0xe)
      .maxstack  1
      .try
      {
        IL_0000:  nop
        IL_0001:  nop
        IL_0002:  leave.s    IL_000c
      }  // end .try
      finally
      {
        IL_0004:  ldarg.0
        IL_0005:  call       instance void [mscorlib]System.Object::Finalize()
        IL_000a:  nop
        IL_000b:  endfinally
      }  // end handler
      IL_000c:  nop
      IL_000d:  ret
    } // end of method Program::Finalize 

    使用析构函数来释放资源有几个问题

     

    1、与C++析构函数相比,C#析构函数的问题是他们的不确定性。在删除C++对象时,其析构函数会立即执行,但是由于垃圾收集器的工作方式,无法确定C#对象的析构函数何时执行。

    2、C#析构函数的执行会延迟对象最终从内存中删除的时间。有析构函数的对象需要2次处理才能删除:第一次调用析构函数时,没有删除对象,第二次调用才真正删除对象。

     

    二、IDisposable接口

    IDisposable接口定义了一个模式,为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾函数器相关的问题。IDisposable接口声明了一个方法Dispose(),它不带参数,返回void。

     

    1、MSDN建议按照下面的模式实现IDisposable接口

        public class Foo : IDisposable
    
        {
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            /// <summary>
            /// Dispose(bool disposing) 执行分两种不同的情况.
            /// 如果disposing 等于 true, 方法已经被调用
            /// 或者间接被用户代码调用. 托管和非托管的代码都能被释放
            /// 如果disposing 等于false, 方法已经被终结器 finalizer 从内部调用过,
            /// 你就不能在引用其他对象,只有非托管资源可以被释放。
            /// </summary>
            /// <param name="disposing"></param>
            protected virtual void Dispose(bool disposing)
            {
                if (!m_disposed)
                {
                    if (disposing)
                    {
                        // Release managed resources
                    }
    
                    // Release unmanaged resources
    
                    m_disposed = true;
                }
            }
    
            ~Foo()
            {
                Dispose(false);
            }
    
            private bool m_disposed;
        }

    在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize

     

    (1)、Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的

     

    (2)、void Dispose(bool disposing)函数通过一个disposing参数来区别当前是否是被Dispose()调用

    如果是被Dispose()调用,那么需要同时释放托管和非托管的资源。如果是被~Foo()(也就是C#的Finalize())调用了,那么只需要释放非托管的资源即可。

     

    (3)、Dispose()函数是被其它代码显式调用并要求释放资源的,而Finalize是被GC调用的

    在GC调用的时候Foo所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize。

     

    然而,即使重复调用Finalize和Dispose也是不存在问题的,因为有变量m_disposed的存在,资源只会被释放一次,多余的调用会被忽略过去。

     

    Finalize、Dispose保证了

     

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

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

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

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

    namespace 资源回收
    
    {
        class Program
        {
            static void Main(string[] args)
            {
                //使用using对实现IDisposable的类了进行资源管理
    /*拿到一个对象的时候,首先判断这个对象是否实现了IDisposable接口,如果实现了,最好就用using包裹住这个对象,保证这个对象用完之后被释放掉,否则很可能出现资源泄露的问题
    */
                using (Telphone t1 = new Telphone())
                {
                    t1.Open();
                    t1.Speak("hello");
                    t1.Bomb();
                    //t1.Dispose();//如果在这里调用了Dispose()方法释放资源,那么在执行t1.Open()方法就出错,电话线已经被剪断了,无法再打电话了
                    t1.Open();
                    t1.Speak("I am back!");
                }//代码执行到这里后,就会调用Dispose方法来进行资源回收
                Console.ReadKey();
            }
        }
        /// <summary>
        /// Telphone类实现了IDisposable接口
        /// </summary>
        class Telphone : IDisposable
        {
            /// <summary>
            /// 电话状态
            /// </summary>
            private TelphoneState state;
            /// <summary>
            /// 打电话
            /// </summary>
            public void Open()
            {
                if (state == TelphoneState.Disposed)
                {
                    throw new Exception("电话线已经被剪断,无法打开!");
                }
                state = TelphoneState.Open;
                Console.WriteLine("拿起电话");
            }
            /// <summary>
            /// 说话
            /// </summary>
            /// <param name="s">说话内容</param>
            public void Speak(string s)
            {
                if (state != TelphoneState.Open)
                {
                    throw new Exception("没有连接");
                }
                Console.WriteLine(s);
            }
            /// <summary>
            /// 挂掉电话
            /// </summary>
            public void Bomb()
            {
                state = TelphoneState.Close;
                Console.WriteLine("挂掉电话");
            }
            #region IDisposable 成员
            /// <summary>
            /// 实现IDisposable接口中的Dispose()方法来释放非托管资源
            /// 如何释放非托管资源由程序自己定
            /// </summary>
            public void Dispose()
            {
                if (state == TelphoneState.Open)
                {
                    Bomb();//挂掉电话
                }
                state = TelphoneState.Disposed;
                Console.WriteLine("剪断电话线");
            }
            #endregion
        }
        /// <summary>
        /// 电话状态枚举
        /// </summary>
        enum TelphoneState
        {
            Open, Close, Disposed
        }
    }

    三、析构函数和IDisposable混合调用的例子

     

    IDisposable例子

    public class ResourceHolder : IDisposable
    
    {
          private bool isDispose = false;
          
          // 显示调用的Dispose方法
      public void Dispose() 
          {
               Dispose(true);
              GC.SuppressFinalize(this); 
           }
    
           // 实际的清除方法
      protected virtual void Dispose(bool disposing) 
          {
                if (!isDisposed)
               {
                  if (disposing) 
               { 
                          // 这里执行清除托管对象的操作.
                      }
                      // 这里执行清除非托管对象的操作
                }
        
             isDisposed=true;
          }
    
          // 析构函数 
          ~ResourceHolder()
          {
                Dispose (false);
          }
    }
  • 相关阅读:
    nginx-1.8.1的安装
    ElasticSearch 在3节点集群的启动
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    sqoop导入导出对mysql再带数据库test能跑通用户自己建立的数据库则不行
    LeetCode 501. Find Mode in Binary Search Tree (找到二叉搜索树的众数)
    LeetCode 437. Path Sum III (路径之和之三)
    LeetCode 404. Sum of Left Leaves (左子叶之和)
    LeetCode 257. Binary Tree Paths (二叉树路径)
    LeetCode Questions List (LeetCode 问题列表)- Java Solutions
    LeetCode 561. Array Partition I (数组分隔之一)
  • 原文地址:https://www.cnblogs.com/superfeeling/p/13473700.html
Copyright © 2011-2022 走看看