国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

c# -- 對象銷毀和垃圾回收

2019-11-17 03:04:12
字體:
來源:轉載
供稿:網友

c# -- 對象銷毀和垃圾回收

有些對象需要顯示地銷毀代碼來釋放資源,比如打開的文件資源,鎖,操作系統(tǒng)句柄和非托管對象。在.NET中,這就是所謂的對象銷毀,它通過IDisposal接口來實現(xiàn)。不再使用的對象所占用的內存管理,必須在某個時候回收;這個被稱為無用單元收集的功能由CLR執(zhí)行。

對象銷毀和垃圾回收的區(qū)別在于:對象銷毀通常是明確的策動;而垃圾回收完全是自動地。換句話說,程序員負責釋放文件句柄,鎖,以及操作系統(tǒng)資源;而CLR負責釋放內存。

本章將討論對象銷毀和垃圾回收,還描述了C#處理銷毀的一個備選方案--Finalizer及其模式。最后,我們討論垃圾回收器和其他內存管理選項的復雜性。

對象銷毀 垃圾回收
1)IDisposal接口2) Finalizer 垃圾回收
對象銷毀用于釋放非托管資源 垃圾回收用于自動釋放不再被引用的對象所占用的內存;并且垃圾回收什么時候執(zhí)行時不可預計的
為了彌補垃圾回收執(zhí)行時間的不確定性,可以在對象銷毀時釋放托管對象占用的內存

IDisposal,Dispose和Close

image

.NET Framework定義了一個特定的接口,類型可以使用該接口實現(xiàn)對象的銷毀。該接口的定義如下:

public interface IDisposable{void Dispose();}

C#提供了鴘語法,可以便捷的調用實現(xiàn)了IDisposable的對象的Dispose方法。比如:

using (FileStream fs = new FileStream ("myFile.txt", FileMode.Open)){// ... Write to the file ...}

編譯后的代碼與下面的代碼是一樣的:

FileStream fs = new FileStream ("myFile.txt", FileMode.Open);try{// ... Write to the file ...}finally{if (fs != null) ((IDisposable)fs).Dispose();}

finally語句確保了Dispose方法的調用,及時發(fā)生了異常,或者代碼在try語句中提前返回。

在簡單的場景中,創(chuàng)建自定義的可銷毀的類型值需要實現(xiàn)IDisposable接口即可

sealed class Demo : IDisposable{public void Dispose(){// Perform cleanup / tear-down....}}

請注意,對于sealed類,上述模式非常適合。在本章后面,我們會介紹另外一種銷毀對象的模式。對于非sealed類,我們強烈建議時候后面的那種銷毀對象模式,否則在非sealed類的子類中,也希望實現(xiàn)銷毀時,會發(fā)生非常詭異的問題。

對象銷毀的標準語法

Framework在銷毀對象的邏輯方面遵循一套規(guī)則,這些規(guī)則并不限用于.NET Framework或C#語言;這些規(guī)則的目的是定義一套便于使用的協(xié)議。這些協(xié)議如下:

  • 一旦銷毀,對象不可恢復。對象不能被再次激活,調用對象的方法或者屬性拋出ObjectDisposedException異常
  • 重復地調用對象的Disposal方法會導致錯誤
  • 如果一個可銷毀對象x包含,或包裝,或處理另外一個可銷毀對象y,那么x的Dispose方法自動調用x的Dispose方法,除非另有指令(不銷毀y)

這些規(guī)則同樣也適用于我們平常創(chuàng)建自定義類型,盡管它并不是強制性的。沒有誰能阻止你編寫一個不可銷毀的方法;然而,這么做,你的同事也許會用高射炮攻擊你。

對于第三條規(guī)則,一個容器對象自動銷毀其子對象。最好的一個例子就是,windows容器對象比如Form對著Panel。一個容器對象可能包含多個子控件,那你也不需要顯示地銷毀每個字對象:關閉或銷毀父容器會自動關閉其子對象。另外一個例子就是如果你在DeflateStream包裝了FileStream,那么銷毀DeflateStream時,F(xiàn)ileStream也會被銷毀--除非你在構造器中指定了其他的指令。

Close和Stop

有一些類型除了Dispose方法之外,還定義了Close方法。Framework對于Close方法并沒有保持完全一致性,但在幾乎所有情況下,它可以:

  • 要么在功能上與Dispose一致
  • 或只是Dispose的一部分功能

對于后者一個典型的例子就是IDbConnecton類型,一個Closed的連接可以再次被打開;而一個Disposed的連接對象則不能。另外一個例子就是Windows程序使用ShowDialog的激活某個窗口對象:Close方法隱藏該窗口;而Dispose釋放窗口所使用的資源。

有一些類定義Stop方法(比如Timer或HttpListener)。與Dipose方法一樣,Stop方法可能會釋放非托管資源;但是與Dispose方法不同的是,它允許重新啟動。

何時銷毀對象

銷毀對象應該遵循的規(guī)則是“如有疑問,就銷毀”。一個可以被銷毀的對象--如果它可以說話--那么將會說這些內容:

“如果你結束對我的使用,那么請讓我知道。如果只是簡單地拋棄我,我可能會影響其他實例對象、應用程序域、計算機、網絡、或者數(shù)據(jù)庫

如果對象包裝了非托管資源句柄,那么經常會要求銷毀,以釋放句柄。例子包括Windows Form控件、文件流或網絡流、網絡sockets,GDI+畫筆、GDI+刷子,和bitmaps。與之相反,如果一個類型是可銷毀的,那么它會經常(但不總是)直接或間接地引用非托管句柄。這是由于非托管句柄對操作系統(tǒng)資源,網絡連接,以及數(shù)據(jù)庫鎖之外的世界提供了一個網關(出入口),這就意味著使用這些對象時,如果不正確的銷毀,那么會對外面的世界代碼麻煩。

但是,遇到下面三種情形時,要銷毀對象

  • 通過靜態(tài)成員或屬性獲取一個共享的對象
  • 如果一個對象的Dispose方法與你的期望不一樣
  • 從設計的角度看,如果一個對象的Dispose方法不必要,且銷毀對象給程序添加了復雜度

第一種情況很少見。多數(shù)情形都可以在System.Drawing命名空間下找到:通過靜態(tài)成員或屬性獲取的GDI+對象(比如Brushed.Blue)就不能銷毀,這是因為該實現(xiàn)在程序的整個生命周期中都會用到。而通過構造器得到的對象實例,比如new SolidBrush,就應該銷毀,這同樣適用于通過靜態(tài)方法獲取的實例對象(比如Font.FromHdc)。

第二種情況就比較常見。下表以System.IO和System.Data命名空間下類型舉例說明

類型銷毀功能何時銷毀
MemoryStream防止對I/O繼續(xù)操作當你需要再次讀讀或寫流
StreamReader,StreamWriter清空reader/writer,并關閉底層的流當你希望底層流保持打開時(一旦完成,你必須改為調用StreamWriter的Flush方法)
IDbConnection釋放數(shù)據(jù)庫連接,并清空連接字符串如果你需要重新打開數(shù)據(jù)庫連接,你需要調用Close方法而不是Dispose方法
DataContext(LINQ to SQL)防止繼續(xù)使用當你需要延遲評估連接到Context的查詢

第三者情況包含了System.ComponentModel命名空間下的這幾個類:WebClient, StringReader, StringWriter和BackgroundWorker。這些類型有一個共同點,它們之所以是可銷毀的是源于它們的基類,而不是真正的需要進行必要的清理。如果你需要在一個方法中使用這樣的類型,那么在using語句中實例化它們就可以了。但是,如果實例對象需要持續(xù)一段較長的時間,并記錄何時不再使用它們以銷毀它們,就會給程序帶來不惜要的復雜度。在這樣的情況下,那么你就應該忽略銷毀對象。

選擇性地銷毀對象

正因為IDisposable實現(xiàn)類可以使用using語句來實例化,因而這可能很容易導致該實現(xiàn)類的Dispose方法延伸至不必要的行為。比如:

public sealed class HouseManager : IDisposable{public void Dispose(){CheckTheMail();}...}

想法是該類的使用者可以選擇避免不必要的清理--簡單地說就是不調用Dispose方法。但是,這就需要調用者知道HouseManager類Dispose方法的實現(xiàn)細節(jié)。及時是后續(xù)添加了必要的清理行為也破壞了規(guī)則。

public void Dispose(){CheckTheMail(); // NonessentialLockTheHouse(); // Essential}

在這種情況下,就應該使用選擇性銷毀模式

public sealed class HouseManager : IDisposable{public readonly bool CheckMailOnDispose;public Demo (bool checkMailOnDispose){CheckMailOnDispose = checkMailOnDispose;}public void Dispose(){if (CheckMailOnDispose) CheckTheMail();LockTheHouse();}...}

這樣,任何情況下,調用者都可以調用Dispose--上述實現(xiàn)不僅簡單,而且避免了特定的文檔或通過反射查看Dispose的細節(jié)。這種模式在.net中也有實現(xiàn)。System.IO.ComPRession空間下的DeflateStream類中,它的構造器如下

public DeflateStream (Stream stream, CompressionMode mode, bool leaveOpen)

非必要的行為就是在銷毀對象時關閉內在的流(第一個參數(shù))。有時候,你希望內部流保持打開的同時并銷毀DeflateStream以執(zhí)行必要的銷毀行為(清空bufferred數(shù)據(jù))

這種模式看起來簡單,然后直到Framework 4.5,它才從StreamReader和StreamWriter中脫離出來。結果卻是丑陋的:StreamWriter必須暴露另外一個方法(Flush)以執(zhí)行必要的清理,而不是調用Dispose方法(Framework 4.5在這兩個類上公開一個構造器,以允許你保持流處于打開狀態(tài))。System.Security.Cryptography命名空間下的CryptoStream類,也遭遇了同樣的問題,當需要保持內部流處于打開時你要調用FlushFinalBlock銷毀對象。

銷毀對象時清除字段

在一般情況下,你不要在對象的Dispose方法中清除該對象的字段。然而,銷毀對象時,應該取消該對象在生命周期內所有訂閱的事件。退訂這些事件避免了接收到非期望的通知--同時也避免了垃圾回收器繼續(xù)對該對象保持監(jiān)視。

設置一個字段用以指明對象是否銷毀,以便在使用者在該對象銷毀后訪問該對象拋出一個ObjectDisposedException,這是非常值得做的。一個好的模式就是使用一個public的制度的屬性:

public bool IsDisposed { get; private set; }

盡管技術上沒有必要,但是在Dispose方法清除一個對象所擁有的事件句柄(把句柄設置為null)也是非常好的一種實踐。這消除了在銷毀對象期間這些事件被觸發(fā)的可能性。

偶爾,一個對象擁有高度秘密,比如加密密鑰。在這種情況下,那么在銷毀對象時清除這樣的字段就非常有意義(避免被非授權組件或惡意軟件發(fā)現(xiàn))。System.Security.Cryptography命令空間下的SymmetricAlgorithm類就屬于這種情況,因此在銷毀該對象時,調用Array.Clear方法以清除加密密鑰。

自動垃圾回收機制

無論一個對象是否需要Dispose方法以實現(xiàn)銷毀對象的邏輯,在某個時刻,該對象在堆上所占用的內存空間必須釋放。這一切都是由CLR通過GC自動處理. 你不需要自己釋放托管內存。我們首先來看下面的代碼

public void Test(){byte[] myArray = new byte[1000];}

當Test方法執(zhí)行時,在內存的堆上分配1000字節(jié)的一個數(shù)組;該數(shù)組被變量myArray引用,這個變量存儲在變量棧上。當方法退出后,局部變量myArray就失去了存在的范疇,這也意味著沒有引用指向內存堆上的數(shù)組。那么該孤立的數(shù)組,就非常適合通過垃圾回收機制進行回收。

垃圾回收機制并不會在一個對象變成孤立的對象之后就立即執(zhí)行。與大街上的垃圾收集不一樣,.net垃圾回收是定期執(zhí)行,盡享不是按照一個估計的計劃。CLR決定何時進行垃圾回收,它取決于許多因素,比如,剩余內存,已經分配的內存,上一次垃圾回收的時間。這就意味著,在一個對象被孤立后到期占用的內存被釋放之間,有一個不確定的時間延遲。該延遲的范圍可以從幾納秒到數(shù)天。

垃圾回收和內存占用垃圾收集試圖在執(zhí)行垃圾回收的時間與程序的內存占用之間建立一個平衡。因此,程序可以占用比它們實際需要更多的內存,尤其特現(xiàn)在程序創(chuàng)建的大的臨時數(shù)組。你可以通過Windows任務管理器監(jiān)視某一個進程內存的占用,或者通過編程的方式查詢性能計數(shù)器來監(jiān)視內存占用:
// These types are in System.Diagnostics:string procName = Process.GetCurrentProcess().ProcessName;using (PerformanceCounter pc = new PerformanceCounter("Process", "Private Bytes", procName))Console.WriteLine (pc.NextValue());
上面的代碼查詢內部工作組,返回你當前程序的內存占用。尤其是,該結果包含了CLR內部釋放,以及把這些資源讓給操作系統(tǒng)以供其他的進程使用。

根就是指保持對象依然處于活著的事物。如果一個對象不再直接或間接地被一個根引用,那么該對象就適合于垃圾回收。

一個跟可以是:

  • 一個正在執(zhí)行的方法的局部變量或參數(shù)(或者調用棧中任意方法的局部變量或參數(shù))
  • 一個靜態(tài)變量
  • 存貯在結束隊列中的一個對象

正在執(zhí)行的代碼可能涉及到一個已經刪除的對象,因此,如果一個實例方法正在執(zhí)行,那么該實例方法的對象必然按照上述方式被引用。

請注意,一組相互引用的對象的循環(huán)被視作無根的引用。換一種方式,也就是說,對象不能通過下面的箭頭指向(引用)而從根獲取,這也就是引用無效,因此這些對象也將被垃圾回收器處理。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 九龙城区| 吴江市| 荃湾区| 台中县| 松原市| 若羌县| 枣阳市| 且末县| 深泽县| 将乐县| 江华| 高青县| 镇江市| 虎林市| 北安市| 九寨沟县| 南京市| 会同县| 南平市| 兴安盟| 白玉县| 庐江县| 屏东市| 宿州市| 德阳市| 修水县| 十堰市| 甘肃省| 泰兴市| 泉州市| 扎鲁特旗| 乌鲁木齐市| 平度市| 金华市| 冀州市| 玛曲县| 时尚| 杭锦后旗| 太保市| 申扎县| 工布江达县|