您好,登錄后才能下訂單哦!
本篇內容主要講解“.net非托管資源如何回收”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“.net非托管資源如何回收”吧!
本文實例講述了.net非托管資源的回收方法,分享給大家供大家參考。具體分析如下:
釋放未托管的資源有兩種方法
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);
}
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例子
復制代碼 代碼如下:
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("掛掉電話");
}
IDisposable 成員
}
/// <summary>
/// 電話狀態枚舉
/// </summary>
enum TelphoneState
{
Open, Close, Disposed
}
}
程序運行結果如下圖所示:
三、析構函數和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);
}
}
到此,相信大家對“.net非托管資源如何回收”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。