本文實(shí)例講述了.net非托管資源的回收方法,分享給大家供大家參考。具體分析如下:
釋放未托管的資源有兩種方法
1、析構(gòu)函數(shù)
2、實(shí)現(xiàn)System.IDisposable接口
一、析構(gòu)函數(shù)
構(gòu)造函數(shù)可以指定必須在創(chuàng)建類(lèi)的實(shí)例時(shí)進(jìn)行的某些操作,在垃圾收集器刪除對(duì)象時(shí),也可以調(diào)用析構(gòu)函數(shù)。析構(gòu)函數(shù)初看起來(lái)似乎是放置釋放未托管資源、執(zhí)行一般清理操作的代碼的最佳地方。但是,事情并不是如此簡(jiǎn)單。由于垃圾回收器的運(yùn)行規(guī)則決定了,不能在析構(gòu)函數(shù)中放置需要在某一時(shí)刻運(yùn)行的代碼,如果對(duì)象占用了寶貴而重要的資源,應(yīng)盡可能快地釋放這些資源,此時(shí)就不能等待垃圾收集器來(lái)釋放了.
實(shí)例
復(fù)制代碼 代碼如下:
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中,你會(huì)發(fā)現(xiàn)并沒(méi)有這個(gè)析構(gòu)的方法。C#編譯器在編譯析構(gòu)函數(shù)時(shí),會(huì)隱式地把析構(gòu)函數(shù)的代碼編譯為Finalize()方法的對(duì)應(yīng)代碼,確保執(zhí)行父類(lèi)的Finalize()方法 看下這段代碼中對(duì)于析構(gòu)函數(shù)的編譯:
復(fù)制代碼 代碼如下:
.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
使用析構(gòu)函數(shù)來(lái)釋放資源有幾個(gè)問(wèn)題:
1、與C++析構(gòu)函數(shù)相比,C#析構(gòu)函數(shù)的問(wèn)題是他們的不確定性。在刪除C++對(duì)象時(shí),其析構(gòu)函數(shù)會(huì)立即執(zhí)行,但是由于垃圾收集器的工作方式,無(wú)法確定C#對(duì)象的析構(gòu)函數(shù)何時(shí)執(zhí)行。
2、C#析構(gòu)函數(shù)的執(zhí)行會(huì)延遲對(duì)象最終從內(nèi)存中刪除的時(shí)間。有析構(gòu)函數(shù)的對(duì)象需要2次處理才能刪除:第一次調(diào)用析構(gòu)函數(shù)時(shí),沒(méi)有刪除對(duì)象,第二次調(diào)用才真正刪除對(duì)象。
二、IDisposable接口
IDisposable接口定義了一個(gè)模式,為釋放未托管的資源提供了確定的機(jī)制,并避免產(chǎn)生析構(gòu)函數(shù)固有的與垃圾函數(shù)器相關(guān)的問(wèn)題。IDisposable接口聲明了一個(gè)方法Dispose(),它不帶參數(shù),返回void。
1、MSDN建議按照下面的模式實(shí)現(xiàn)IDisposable接口
復(fù)制代碼 代碼如下:
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的對(duì)象中實(shí)際上有兩個(gè)用于釋放資源的函數(shù):Dispose和Finalize
(1)、Finalize的目的是用于釋放非托管的資源,而Dispose是用于釋放所有資源,包括托管的和非托管的
(2)、void Dispose(bool disposing)函數(shù)通過(guò)一個(gè)disposing參數(shù)來(lái)區(qū)別當(dāng)前是否是被Dispose()調(diào)用
如果是被Dispose()調(diào)用,那么需要同時(shí)釋放托管和非托管的資源。如果是被~Foo()(也就是C#的Finalize())調(diào)用了,那么只需要釋放非托管的資源即可。
(3)、Dispose()函數(shù)是被其它代碼顯式調(diào)用并要求釋放資源的,而Finalize是被GC調(diào)用的
在GC調(diào)用的時(shí)候Foo所引用的其它托管對(duì)象可能還不需要被銷(xiāo)毀,并且即使要銷(xiāo)毀,也會(huì)由GC來(lái)調(diào)用。因此在Finalize中只需要釋放非托管資源即可。另外一方面,由于在Dispose()中已經(jīng)釋放了托管和非托管的資源,因此在對(duì)象被GC回收時(shí)再次調(diào)用Finalize是沒(méi)有必要的,所以在Dispose()中調(diào)用GC.SuppressFinalize(this)避免重復(fù)調(diào)用Finalize。
然而,即使重復(fù)調(diào)用Finalize和Dispose也是不存在問(wèn)題的,因?yàn)橛凶兞縨_disposed的存在,資源只會(huì)被釋放一次,多余的調(diào)用會(huì)被忽略過(guò)去。
Finalize、Dispose保證了:
(1)、 Finalize只釋放非托管資源;
(2)、 Dispose釋放托管和非托管資源;
(3)、 重復(fù)調(diào)用Finalize和Dispose是沒(méi)有問(wèn)題的;
(4)、 Finalize和Dispose共享相同的資源釋放策略,因此他們之間也是沒(méi)有沖突的。
2、IDisposable例子
復(fù)制代碼 代碼如下:
namespace 資源回收
{
class Program
{
static void Main(string[] args)
{
//使用using對(duì)實(shí)現(xiàn)IDisposable的類(lèi)了進(jìn)行資源管理
/*拿到一個(gè)對(duì)象的時(shí)候,首先判斷這個(gè)對(duì)象是否實(shí)現(xiàn)了IDisposable接口,如果實(shí)現(xiàn)了,最好就用using包裹住這個(gè)對(duì)象,保證這個(gè)對(duì)象用完之后被釋放掉,否則很可能出現(xiàn)資源泄露的問(wèn)題
*/
using (Telphone t1 = new Telphone())
{
t1.Open();
t1.Speak("hello");
t1.Bomb();
//t1.Dispose();//如果在這里調(diào)用了Dispose()方法釋放資源,那么在執(zhí)行t1.Open()方法就出錯(cuò),電話線已經(jīng)被剪斷了,無(wú)法再打電話了
t1.Open();
t1.Speak("I am back!");
}//代碼執(zhí)行到這里后,就會(huì)調(diào)用Dispose方法來(lái)進(jìn)行資源回收
Console.ReadKey();
}
}
/// summary>
/// Telphone類(lèi)實(shí)現(xiàn)了IDisposable接口
/// /summary>
class Telphone : IDisposable
{
/// summary>
/// 電話狀態(tài)
/// /summary>
private TelphoneState state;
/// summary>
/// 打電話
/// /summary>
public void Open()
{
if (state == TelphoneState.Disposed)
{
throw new Exception("電話線已經(jīng)被剪斷,無(wú)法打開(kāi)!");
}
state = TelphoneState.Open;
Console.WriteLine("拿起電話");
}
/// summary>
/// 說(shuō)話
/// /summary>
/// param name="s">說(shuō)話內(nèi)容/param>
public void Speak(string s)
{
if (state != TelphoneState.Open)
{
throw new Exception("沒(méi)有連接");
}
Console.WriteLine(s);
}
/// summary>
/// 掛掉電話
/// /summary>
public void Bomb()
{
state = TelphoneState.Close;
Console.WriteLine("掛掉電話");
}
IDisposable 成員
}
/// summary>
/// 電話狀態(tài)枚舉
/// /summary>
enum TelphoneState
{
Open, Close, Disposed
}
}
程序運(yùn)行結(jié)果如下圖所示:
三、析構(gòu)函數(shù)和IDisposable混合調(diào)用的例子
復(fù)制代碼 代碼如下:
public class ResourceHolder : IDisposable
{
private bool isDispose = false;
// 顯示調(diào)用的Dispose方法
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// 實(shí)際的清除方法
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
// 這里執(zhí)行清除托管對(duì)象的操作.
}
// 這里執(zhí)行清除非托管對(duì)象的操作
}
isDisposed=true;
}
// 析構(gòu)函數(shù)
~ResourceHolder()
{
Dispose (false);
}
}
希望本文所述對(duì)大家的asp.net程序設(shè)計(jì)有所幫助。
您可能感興趣的文章:- ASP.NET主機(jī)資源控制的一些心得
- asp.net(C#) 開(kāi)源資源大匯總
- asp.net中資源文件的使用
- 實(shí)例講解.NET中資源文件的創(chuàng)建與使用
- asp.net 組件開(kāi)發(fā)中的內(nèi)嵌資源引用
- .NET 資源文件resx、Resources詳細(xì)說(shuō)明
- ASP.NET 服務(wù)器路徑和一般資源調(diào)用
- 詳談.net中的垃圾回收機(jī)制
- asp.net中virtual和abstract的區(qū)別分析
- ASP.NET實(shí)現(xiàn)將word文檔轉(zhuǎn)換成pdf的方法
- ASP.NET配置文件Web.config用法詳解
- ASP.net全局程序文件Global.asax用法分析