釋放類所使用的未託管資源的兩種方式: 程序員
1.利用運行庫強制執行的析構函數,但析構函數的執行是不肯定的,並且,因爲垃圾收集器的工做方式,它會給運行庫增長不可接受的系統開銷。 數據庫
2.IDisposable接口提供了一種機制,容許類的用戶控制釋放資源的時間,但須要確保執行Dispose()。數組
通常狀況下,最好的方法是執行這兩種機制,得到這兩種機制的優勢,克服其缺點。假定大多數程序員都能正確調用Dispose(),實現IDisposable接口,同時把析構函數做爲一種安全的機制,以防沒有調用Dispose()。緩存
ispose()有第二個protected重載方法,它帶一個bool參數,這是真正完成清理工做的方法。Dispose(bool)由析構函數和IDisposable.Dispose()調用。這個方式的重點是確保全部的清理代碼都放在一個地方。 安全
傳遞給Dispose(bool)的參數表示Dispose(bool)是由析構函數調用,仍是由IDisposable.Dispose()調用——Dispose(bool)不該從代碼的其餘地方調用,其緣由是: 函數
● 若是客戶調用IDisposable.Dispose(),該客戶就指定應清理全部與該對象相關的資源,包括託管和非託管的資源。 性能
● 若是調用了析構函數,在原則上,全部的資源仍須要清理。可是在這種狀況下,析構函數必須由垃圾收集器調用,並且不該訪問其餘託管的對象,由於咱們再也不能肯定它們的狀態了。在這種狀況下,最好清理已知的未託管資源,但願引用的託管對象還有析構函數,執行本身的清理過程。 this
isDispose成員變量表示對象是否已被刪除,並容許確保很少次刪除成員變量。這個簡單的方法不是線程安全的,須要調用者確保在同一時刻只有一個線程調用方法。要求客戶進行同步是一個合理的假定,在整個.NET類庫中反覆使用了這個假定(例如在集合類中)。spa
最後,IDisposable.Dispose()包含一個對System.GC. SuppressFinalize()方法的調用。SuppressFinalize()方法則告訴垃圾收集器有一個類再也不須要調用其析構函數了。由於Dispose()已經完成了全部須要的清理工做,因此析構函數不須要作任何工做。調用SuppressFinalize()就意味着垃圾收集器認爲這個對象根本沒有析構函數 .net
[csharp] view plain copy
1.被分配內存空間的對象最有可能被釋放。在方法執行時,就須要爲該方法的對象分配內存空間,搜索最近分配的對象集合有助於花費最少的代價來儘量多地釋放內存空間。
2.生命期最長的對象釋放的可能性最小,通過幾輪垃圾回收後,對象仍然存在,搜索它時就須要進行大量的工做,卻只能釋放很小的一部分空間。
3.同時被分配內存的對象一般是同時使用,將它們彼此相連有助於提升緩存性能和回收效率。
C#中的回收器是分代的垃圾回收器(Gererational Garbage Collector) 它將分配的對象分爲3個類別或代。(可用GC.GetGeneration方法返回任意做爲參數的對象當前所處的代)最近被分配內存的對象被放置於第0 代,由於第0代很小,小到足以放進處理器的二級(L2)緩存,因此它可以提供對對象的快速存取。通過一輪垃圾回收後,仍然保留在第0代中的對象被移進第1 代中,再通過一輪垃圾內存回收後,仍然保留在第1代中的對象則被移進第2代中,第2代中包含了生存期較長的對象。(類比於JAVA中的分代收集器)
在C#中值類型是在堆棧中分配內存,它們有自身的生命週期,因此不用對它們進行管理,會自動分配和釋放。而引用類型是在堆中分配內存的。因此它的分配和釋放就須要像回收機制來管理。C#爲一個對象分配內存時,託管堆能夠當即返回新對象所需的內存,由於託管堆相似於簡單的字節數組,有一個指向第一個可用內存空間的指針,指針像遊標同樣向後移動,一段段內存就分配給了正在運行的程序的對象。在不須要太多垃圾回收的程序中,託管堆性能優於傳統的堆。
當第0代中沒有能夠分配的有效內存時,就觸發了第0代中的一輪垃圾回收,它將刪除那些再也不被引用的對象,並將當前正在使用的對象移至第1代。而當第0代垃圾回收後依然不能請求到充足的內存時,就啓動第1代垃圾回收。若是對各代都進行了垃圾回收後仍沒有可用的內存就會引起一個 OutOfMemoryException異常。
終結器(Finalize方法)
在有些狀況下,類能夠提供一個終結器在對象被銷燬時執行,終結器是一個名爲Finalize的受保護的方法:
protected void Finalize()
{
base.Finalize();
//釋放外部資源
}
垃圾回收器使用名爲「終止隊列」的內部結構跟蹤具備 Finalize 方法的對象。每次您的應用程序建立具備 Finalize 方法的對象時,垃圾回收器都在終止隊列中放置一個指向該對象的項。託管堆中全部須要在垃圾回收器回收其內存以前調用它們的終止代碼的對象都在終止隊列中含有項。(實現Finalize方法或析構函數對性能可能會有負面影響,所以應避免沒必要要地使用它們。用Finalize方法回收對象使用的內存須要至少兩次垃圾回收。當垃圾回收器執行回收時,它只回收沒有終結器的不可訪問對象的內存。這時,它不能回收具備終結器的不可訪問對象。它改成將這些對象的項從終止隊列中移除並將它們放置在標爲準備終止的對象列表中。該列表中的項指向託管堆中準備被調用其終止代碼的對象。垃圾回收器爲此列表中的對象調用Finalize方法,而後將這些項從列表中移除。後來的垃圾回收將肯定終止的對象確實是垃圾,由於標爲準備終止對象的列表中的項再也不指向它們。在後來的垃圾回收中,實際上回收了對象的內存。歸納而言,就是將垃圾回收分爲了三個階段,第一個階段回收沒有Finalize方法或者析構函數的對象,第二個階段調用那些析構函數或者Finalize方法,第三個階段回收那些剛調用了析構函數或者Finalize方法的對象。)
終結器(finalizer)是在回收過程當中,由垃圾回收器調用的方法。如何含有終結器的對象到了G2中,那麼就會須要很是長的時間來回收。事實上,根據應用程序運行時間的長短,對象頗有機會直到應用程序退出以前都不會被回收(特別是其中包含的重要的資源得不得釋放,將會對性能產生很大的影響,好比說數據庫鏈接得不到釋放。)
Dispose方法
在不使用終結器時,能夠考慮使用Dispose方法,你可使用這個方法來釋放所保存包括的在託管對象引用在內的任何資源。系統類中如何實現了Dispose方法,那麼通常Dispose方法中都包含了SuppressFinalize方法,這個方法會告知系統這個類已經再也不須要析構了,這樣能夠提升釋放資源的效率。因此在自定義類中的Dispose方法應該調用GC.SuppressFinalize來告知運行時這些對象不須要析構。以下所示:
public void Dispose(){
object.Dispose();
dbConnection.Dispose();
GC.SuppressFinalize(this);//申明不須要終結
}
建立並使用了Dispose方法的對象,就須要使用完該對象以後調用這些方法,最好是在Finally中調用。
System.GC類
GC類包含了可以使用戶與垃圾回收機制進行互操做的靜態方法,包括髮起新一輪垃圾回收操做的方法。肯定某對象當前所在代的方法及當前分配內存空間的方法。
GC.Collect();//無參時將發起一輪全面的回收。(徹底回收以前,應用程序會中止響應,所以不建議使用。)
GC.Collect(i);//(0<=i<=2)對第i代進行垃圾回收。
GetTotalMemory將返因分配於託管堆上的內存空間總量。當參數爲True時,在計算以前將進行一輪全面的垃圾回收。以下所示:
long totalMemory = System.GC.GetTotalMemory(True);
下面是 在.NET Framework 2.0 版中是新增的公共方法:
通知運行庫在安排垃圾回收時應考慮分配大量的非託管內存
public static void AddMemoryPressure (long bytesAllocated)//bytesAllocated已分配的非託管內存的增量。
返回已經對對象的指定代進行的垃圾回收次數。
public static int CollectionCount (int generation)
通知運行庫已釋放非託管內存,在安排垃圾回收時不須要再考慮它。
public static void RemoveMemoryPressure (long bytesAllocated)
C# 中的析構函數其實是重寫了 System.Object 中的虛方法 Finalize
三種最常的方法以下:
1. 析構函數;(由GC調用,不肯定何時會調用)
2. 繼承IDisposable接口,實現Dispose方法;(能夠手動調用。好比數據庫的鏈接,SqlConnection.Dispose(),由於若是及時釋放會影響數據庫性能。這時候會用到這個,再如:文件的打開,若是不釋放會影響其它操做,如刪除操做。調用Dispose後這個對象就不能再用了,就等着被GC回收。)
3. 提供Close方法。(相似Dispose可是,當調用完Close方法後,能夠經過Open從新打開)
析構函數不能顯示調用,而對於後兩種方法來講,都須要進行顯示調用才能被執行。而Close與Dispose這兩種方法的區別在於,調用完了對象的Close方法後,此對象有可能被從新進行使用;而Dispose方法來講,此對象所佔有的資源須要被標記爲無用了,也就是此對象要被銷燬,不能再被使用。
|
析構函數 |
Dispose方法 |
Close方法 |
意義 |
銷燬對象 |
銷燬對象 |
關閉對象資源 |
調用方式 |
不能被顯示調用,在GC回收是被調用 |
須要顯示調用 或者經過using語句 |
須要顯示調用 |
調用時機 |
不肯定 |
肯定,在顯示調用或者離開using程序塊 |
肯定,在顯示調用時 |
下面提供一個模式來結合上面的 析構函數和Dispose方法。
[csharp] view plain copy
上面的範例達到的目的:
1/ 若是客戶沒有調用Dispose(),未能及時釋放託管和非託管資源,那麼在垃圾回收時,還有機會執行Finalize(),釋放非託管資源,可是形成了非託管資源的未及時釋放的空閒浪費
2/ 若是客戶調用了Dispose(),就能及時釋放了託管和非託管資源,那麼該對象被垃圾回收時,不會執行Finalize(),提升了非託管資源的使用效率並提高了系統性能
最後:
若是類中使用了非託管資源,則要考慮提供Close方法,和Open方法。並在您的Dispose方法中先調用 Close方法。
在使用已經有類時,如SqlConnection。若是暫時不用這個鏈接,能夠考慮用Close()方法。若是不用了就考慮調用Dispose()方法。