生活随笔
收集整理的這篇文章主要介紹了
C#资源释放
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
?????????????????????????C# 資源釋放
終于開(kāi)始動(dòng)手寫(xiě)這篇文章了,有個(gè)網(wǎng)友催了我好幾次,今天終于可以靜下心來(lái)完成它。 便于對(duì)文章的開(kāi)展,需要先明確兩個(gè)概念。 第一個(gè)就是很多人用.Net寫(xiě)程序,會(huì)談到托管這個(gè)概念。那么.Net所指的資源托管到底是什么意思,是相對(duì)于所有資源,還是只限于某一方面資源?很多人對(duì)此不是很了解,
其實(shí).Net所指的托管只是針對(duì)內(nèi)存這一個(gè)方面,并不是對(duì)于所有的資源;因此對(duì)于Stream,數(shù)據(jù)庫(kù)的連接,GDI+的相關(guān)對(duì)象,還有Com對(duì)象等等,這些資源并不是受到.Net管理而統(tǒng)稱(chēng)為非托管資源。而對(duì)于內(nèi)存的釋放和回收,系統(tǒng)提供了GC-Garbage Collector,而至于其他資源則需要手動(dòng)進(jìn)行釋放。 那么第二個(gè)概念就是什么是垃圾,通過(guò)我以前的文章,會(huì)了解到.Net類(lèi)型分為兩大類(lèi),一個(gè)就是值類(lèi)型,另一個(gè)就是引用類(lèi)型。前者是分配在棧上,并不需要GC回收;后者是分配在堆上,因此它的內(nèi)存釋放和回收需要通過(guò)GC來(lái)完成。GC的全稱(chēng)為“Garbage Collector”,顧名思義就是垃圾回收器,那么只有被稱(chēng)為垃圾的對(duì)象才能被GC回收。也就是說(shuō),
一個(gè)引用類(lèi)型對(duì)象所占用的內(nèi)存需要被GC回收,需要先成為垃圾。那么.Net如何判定一個(gè)引用類(lèi)型對(duì)象是垃圾呢,.Net的判斷很簡(jiǎn)單,只要判定此對(duì)象或者其包含的子對(duì)象沒(méi)有任何引用是有效的,那么系統(tǒng)就認(rèn)為它是垃圾。 明確了這兩個(gè)基本概念,接下來(lái)說(shuō)說(shuō)GC的運(yùn)作方式以及其的功能。內(nèi)存的釋放和回收需要伴隨著程序的運(yùn)行,因此系統(tǒng)為GC安排了獨(dú)立的線(xiàn)程。那么GC的工作大致是,查詢(xún)內(nèi)存中對(duì)象是否成為垃圾,然后對(duì)垃圾進(jìn)行釋放和回收。那么對(duì)于GC對(duì)于內(nèi)存回收采取了一定的優(yōu)先算法進(jìn)行輪循回收內(nèi)存資源。其次,
對(duì)于內(nèi)存中的垃圾分為兩種,一種是需要調(diào)用對(duì)象的析構(gòu)函數(shù),另一種是不需要調(diào)用的。GC對(duì)于前者的回收需要通過(guò)兩步完成,第一步是調(diào)用對(duì)象的析構(gòu)函數(shù),第二步是回收內(nèi)存,但是要注意這兩步不是在GC一次輪循完成,即需要兩次輪循;相對(duì)于后者,則只是回收內(nèi)存而已。 很明顯得知,對(duì)于某個(gè)具體的資源,無(wú)法確切知道,對(duì)象析構(gòu)函數(shù)什么時(shí)候被調(diào)用,以及GC什么時(shí)候會(huì)去釋放和回收它所占用的內(nèi)存。那么對(duì)于從C、C++之類(lèi)語(yǔ)言轉(zhuǎn)換過(guò)來(lái)的程序員來(lái)說(shuō),這里需要轉(zhuǎn)變觀(guān)念。 那么對(duì)于程序資源來(lái)說(shuō),我們應(yīng)該做些什么,以及如何去做,才能使程序效率最高,同時(shí)占用資源能盡快的釋放。前面也說(shuō)了,資源分為兩種,托管的內(nèi)存資源,這是不需要我們操心的,系統(tǒng)已經(jīng)為我們進(jìn)行管理了;那么對(duì)于非托管的資源,這里再重申一下,就是Stream,數(shù)據(jù)庫(kù)的連接,GDI+的相關(guān)對(duì)象,還有Com對(duì)象等等這些資源,需要我們手動(dòng)去釋放。 如何去釋放,應(yīng)該把這些操作放到哪里比較好呢。.Net提供了三種方法,也是最常見(jiàn)的三種,大致如下: <!--[if !supportLists]-->1.?<!--[endif]-->析構(gòu)函數(shù); <!--[if !supportLists]-->2.?<!--[endif]-->繼承IDisposable接口,實(shí)現(xiàn)Dispose方法; <!--[if !supportLists]-->3.?<!--[endif]-->提供Close方法。
經(jīng)過(guò)前面的介紹,可以知道析構(gòu)函數(shù)只能被GC來(lái)調(diào)用的,那么無(wú)法確定它什么時(shí)候被調(diào)用,因此用它作為資源的釋放并不是很合理,因?yàn)橘Y源釋放不及時(shí);但是為了防止資源泄漏,畢竟它會(huì)被GC調(diào)用,因此析構(gòu)函數(shù)可以作為一個(gè)補(bǔ)救方法。而Close與Dispose這兩種方法的區(qū)別在于,調(diào)用完了對(duì)象的Close方法后,此對(duì)象有可能被重新進(jìn)行使用;而Dispose方法來(lái)說(shuō),此對(duì)象所占有的資源需要被標(biāo)記為無(wú)用了,也就是此對(duì)象被銷(xiāo)毀了,不能再被使用。例如,常見(jiàn)SqlConnection這個(gè)類(lèi),當(dāng)調(diào)用完Close方法后,可以通過(guò)Open重新打開(kāi)數(shù)據(jù)庫(kù)連接,當(dāng)徹底不用這個(gè)對(duì)象了就可以調(diào)用Dispose方法來(lái)標(biāo)記此對(duì)象無(wú)用,等待GC回收。明白了這兩種方法的意思后,大家在往自己的類(lèi)中添加的接口時(shí)候,不要歪曲了這兩者意思。 接下來(lái)說(shuō)說(shuō)這三個(gè)函數(shù)的調(diào)用時(shí)機(jī),我用幾個(gè)試驗(yàn)結(jié)果來(lái)進(jìn)行說(shuō)明,可能會(huì)使大家的印象更深。 首先是這三種方法的實(shí)現(xiàn),大致如下:
??? ///<summary> ??? /// The class to show three disposal function ??? ///</summary> ??? public class DisposeClass:IDisposable ??? { ??????? public void Close() ??????? { ??????????? Debug.WriteLine( "Close called!" ); ??????? } ? ??????? ~DisposeClass() ??????? { ??????????? Debug.WriteLine( "Destructor called!" ); ??????? } ? ??????? #region IDisposable Members ? ??????? public void Dispose() ??????? { ??????????? // TODO:?Add DisposeClass.Dispose implementation ??????????? Debug.WriteLine( "Dispose called!" ); ??????? } ? ??????? #endregion ??? } 對(duì)于Close來(lái)說(shuō)不屬于真正意義上的釋放,除了注意它需要顯示被調(diào)用外,我在此對(duì)它不多說(shuō)了。而對(duì)于析構(gòu)函數(shù)而言,不是在對(duì)象離開(kāi)作用域后立刻被執(zhí)行,只有在關(guān)閉進(jìn)程或者調(diào)用GC.Collect方法的時(shí)候才被調(diào)用,參看如下的代碼運(yùn)行結(jié)果。
??????? private void Create() ??????? { ??????????? DisposeClass myClass = new DisposeClass(); ??????? } ? ??????? private void CallGC() ??????? { ??????????? GC.Collect(); ??????? } ? ??????? // Show destructor ??????? Create(); ??????? Debug.WriteLine( "After created!" ); ??????? CallGC(); 運(yùn)行的結(jié)果為:
After created! Destructor called! 顯然在出了
Create函數(shù)外,
myClass對(duì)象的析構(gòu)函數(shù)沒(méi)有被立刻調(diào)用,而是等顯示調(diào)用GC.Collect才被調(diào)用。 對(duì)于Dispose來(lái)說(shuō),也需要顯示的調(diào)用,但是對(duì)于繼承了IDisposable的類(lèi)型對(duì)象可以使用using這個(gè)關(guān)鍵字,這樣對(duì)象的Dispose方法在出了using范圍后會(huì)被自動(dòng)調(diào)用。例如:
??? using( DisposeClass myClass = new DisposeClass() ) ??? { ??????? //other operation here ??? } 如上運(yùn)行的結(jié)果如下:
Dispose called! 那么對(duì)于如上
DisposeClass類(lèi)型的Dispose實(shí)現(xiàn)來(lái)說(shuō),事實(shí)上GC還需要調(diào)用對(duì)象的析構(gòu)函數(shù),
按照前面的GC流程來(lái)說(shuō),GC對(duì)于需要調(diào)用析構(gòu)函數(shù)的對(duì)象來(lái)說(shuō),至少經(jīng)過(guò)兩個(gè)步驟,即首先調(diào)用對(duì)象的析構(gòu)函數(shù),其次回收內(nèi)存。也就是說(shuō),按照上面所寫(xiě)的Dispose函數(shù),雖說(shuō)被執(zhí)行了,但是GC還是需要執(zhí)行析構(gòu)函數(shù),那么一個(gè)完整的Dispose函數(shù),應(yīng)該通過(guò)調(diào)用GC.SuppressFinalize(this )來(lái)告訴GC,讓它不用再調(diào)用對(duì)象的析構(gòu)函數(shù)中。那么改寫(xiě)后的DisposeClass如下: ??? ///<summary> ??? /// The class to show three disposal function ??? ///</summary> ??? public class DisposeClass:IDisposable ??? { ??????? public void Close() ??????? { ??????????? Debug.WriteLine( "Close called!" ); ??????? } ? ??????? ~DisposeClass() ??????? { ??????????? Debug.WriteLine( "Destructor called!" ); ??????? } ? ??????? #region IDisposable Members ? ??????? public void Dispose() ??????? { ??????????? // TODO:?Add DisposeClass.Dispose implementation ??????????? Debug.WriteLine( "Dispose called!" ); ??????????? GC.SuppressFinalize( this ); ??????? } ? ??????? #endregion ??? } 通過(guò)如下的代碼進(jìn)行測(cè)試。
??????? private void Run() ??????? { ??????????? using( DisposeClass myClass = new DisposeClass() ) ??????????? { ??????????????? //other operation here ??????????? } ??????? } ? ??????? private void CallGC() ??????? { ??????????? GC.Collect(); ??? ??? } ??????? // Show destructor ??????? Run(); ??????? Debug.WriteLine( "After Run!" ); ??????? CallGC(); 運(yùn)行的結(jié)果如下:
Dispose called! After Run! 顯然對(duì)象的析構(gòu)函數(shù)沒(méi)有被調(diào)用。通過(guò)如上的實(shí)驗(yàn)以及文字說(shuō)明,大家會(huì)得到如下的一個(gè)對(duì)比表格。
| | 析構(gòu)函數(shù) | Dispose方法 | Close方法 |
| 意義 | 銷(xiāo)毀對(duì)象 | 銷(xiāo)毀對(duì)象 | 關(guān)閉對(duì)象資源 |
| 調(diào)用方式 | 不能被顯示調(diào)用,會(huì)被GC調(diào)用 | 需要顯示調(diào)用 或者通過(guò)using語(yǔ)句 | 需要顯示調(diào)用 |
| 調(diào)用時(shí)機(jī) | 不確定 | 確定,在顯示調(diào)用或者離開(kāi)using程序塊 | 確定,在顯示調(diào)用時(shí) |
那么在定義一個(gè)類(lèi)型的時(shí)候,是否一定要給出這三個(gè)函數(shù)地實(shí)現(xiàn)呢。
我的建議大致如下。 <!--[if !supportLists]-->
1.<!--[endif]-->
提供析構(gòu)函數(shù),避免資源未被釋放,主要是指非內(nèi)存資源; <!--[if !supportLists]-->
2.<!--[endif]-->
對(duì)于Dispose和Close方法來(lái)說(shuō),需要看所定義的類(lèi)型所使用的資源(參看前面所說(shuō)),而決定是否去定義這兩個(gè)函數(shù); <!--[if !supportLists]-->
3.<!--[endif]-->
在實(shí)現(xiàn)Dispose方法的時(shí)候,一定要加上“GC.SuppressFinalize( this )”語(yǔ)句,避免再讓GC調(diào)用對(duì)象的析構(gòu)函數(shù)。 ? C#程序所使用的內(nèi)存是受托管的,但不意味著濫用,好地編程習(xí)慣有利于提高代碼的質(zhì)量以及程序的運(yùn)行效率。
總結(jié)
以上是生活随笔為你收集整理的C#资源释放的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。