程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 說說非托管資源的回收,說說托管資源回收

說說非托管資源的回收,說說托管資源回收

編輯:C#入門知識

說說非托管資源的回收,說說托管資源回收


釋放未托管的資源有兩種方法

 

1、析構函數

2、實現System.IDisposable接口

 

一、析構函數  

構造函數可以指定必須在創建類的實例時進行的某些操作,在垃圾收集器刪除對象時,也可以調用析構函數。析構函數初看起來似乎是放置釋放未托管資源、執行一般清理操作的代碼的最佳地方。但是,事情並不是如此簡單。由於垃圾回收器的運行規則決定了,不能在析構函數中放置需要在某一時刻運行的代碼,如果對象占用了寶貴而重要的資源,應盡可能快地釋放這些資源,此時就不能等待垃圾收集器來釋放了.  

實例

  C# 代碼   復制
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()方法 看下這段代碼中對於析構函數的編譯:

  C# 代碼   復制
.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接口

  C# 代碼   復制
 public class Foo: IDisposable
 {
     public void Dispose()
     {
        Dispose(true);
        GC.SuppressFinalize(this);
     }
  
     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共享相同的資源釋放策略,因此他們之間也是沒有沖突的。

 

 

 

 

2、IDisposable例子

 

  C# 代碼   復制
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混合調用的例子

 

  C# 代碼   復制
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);
      }
}

教一下,什事托管資源,什事非托管資源可否從最基礎的概念上講解一下

托管資源和非托管資源這個要從內存回收上來講,首先一個點很明確:托管資源,.net垃圾回收器自動回收,非托管資源,垃圾回收期沒法自動回收;
另外一點,托管資源由.net核心管理創建,非托管資源是由.net核心調用其他的接口創建,.net無法控制,例如active控件,畫筆、畫刷。這些都是.Net調用系統接口創建的,它管不著,只能由用戶自已釋放。
最後,.Net內核封裝了的,能夠或者有權限自動釋放的就是托管資源;如果是.Net調用外部資源,無法自動釋放的就是非托管資源。
 

c# 非托管資源 這樣回收可以?

Timer是托管對象,Dispsoe方法可以手動調用,但更好的方法是用using塊調用,這個可以避免由於異常導致dipose沒執行到。
如果是COM對象要在dispose中調用Marshal.ReleaseComObject(comobj);確保COM對象被正確回收。
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved