程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Effective C#第二章:.Net資源管理(2)

Effective C#第二章:.Net資源管理(2)

編輯:關於C語言

正如你剛開始了解的, 垃圾回收器的全部責任就是內存管理。但,所有的系統資源都是你自己負責的。 你可以通過給自己的類型定義一個析構函數,來保證釋放一些系統資源。析構函 數是在垃圾回收器把對象從內存移除前,由系統調用的。你可以,也必須這樣來 釋放任何你所占用的非托管資源。對象的析構函數有時是在對象成為垃圾之後調 用的,但是在內存歸還之前。這個非確定的析構函數意味著在你無法控制對象析 構與停止使用之間的關系(譯注:對象的析構與對象的無法引用是兩個完全不同 的概念。關於GC,本人推薦讀者參考一下Jeffrey的".Net框架程序設計(修 訂版)"中討論的垃圾回收器)。對C++來說這是個重大的改變,並且這在設 計上有一個重大的分歧。有經驗的C++程序員寫的類總在構造函數內申請內存並 且在析構函數中釋放它們:

// Good C++, bad C#:
class CriticalSection
{
public:
 // Constructor acquires the system resource.
 CriticalSection( )
 {
   EnterCriticalSection( );
 }
 // Destructor releases system resource.
 ~CriticalSection( )
 {
   ExitCriticalSection( );
 }
};
// usage:
void Func( )
{
 // The lifetime of s controls Access to
 // the system resource.
 CriticalSection s;
 // Do work.
 //...
 // compiler generates call to destructor.
 // code exits critical section.
}

這是一種很常見 的C++風格,它保證資源無異常的釋放。但這在C#裡不工作,至少,與這不同。 明確的析構函數不是.Net環境或者C#的一部份。強行用C++的風格在C#裡使用析 構函數不會讓它正常的工作。在C#裡,析構函數確實是正確的運行了,但它不是 即時運行的。在前面那個例子裡,代碼最終在critical section上,但在C#裡, 當析構函數存在時,它並不是在critical section上。它會在後面的某個未知時 間上運行。你不知道是什麼時候,你也無法知道是什麼時候。

依懶於析 構函數同樣會導致性能上的損失。須要析構的對象在垃圾回收器上放置了一劑性 能毒藥。當GC發現某個對象是垃圾但是須要析構時,它還不能直接從內存上刪除 這個對象。首先,它要調用析構函數,但析構函數的調用不是在垃圾回收器的同 一個線程上運行的。取而代之的是,GC不得不把對象放置到析構隊列中,讓另一 個線程讓執行所有的析構函數。GC繼續它自己的工作,從內存上移除其它的垃圾 。在下一個GC回收時,那些被析構了的對象才會再從內存上移除。圖2.2展示了 三個內存使用不同的GC情況。注意,那些須要析構的對象會待在內存裡,直到下 一次GC回收。

圖2.2 這個順序展示了析構函數在垃圾回收器上起的作用。對象會在 內存裡存在的時間更長,須要啟動另一個線程來運行垃圾回收器。

這用 使你相信:那些須要析構的對象在內存至少多生存一個GC回收循環。但,我是簡 化了這些事。實際上,因為另一個GC的介入(譯注:其實只有一個GC,作者是想 引用回收代的問題。),使得情況比這復雜得多。.Net回收器采用”代 “來優化這個問題。代可以幫助GC來很快的標識那些看上去看是垃圾的對 象。所以從上一次回後開始創建的對象稱為第0代對象,所有那些經過一次GC回 收後還存在的對象稱為第1代對象。所有那些經過2次或者2次以上GC回收後還存 在的對象稱為第2代對象(譯注:因為目前GC只支持3代對象,第0代到第2代,所 以最多只有第2代對象,如果今後GC支持更多的代,那麼會出現更代的對 象,.Net 1.1與2.0都只支持3代,這是MS證實比較合理的數字)。

分代的 目的就是用來區分臨時變量以及一些應用程序的全局變量。第0代對象很可能是 臨時的變量。成員變量,以及一些全局變量很快會成為第1代對象,最終成為第2 代對象。

GC通過限制檢測第1以及第2代對象來優化它的工作。每個GC循 環都檢測第0代對象。粗略假設個GC會超過10次檢測來檢測第0代對象,而要超過 100次來檢測所有對象。再次考慮析構函數的開銷:一個須要析構函數的對象可 能要比一個不用析構函數的對象在內存裡多待上9個GC回收循環。如果它還沒有 被析構,它將會移到第2代對象。在第2代對象中,一個可以生存上100個GC循環 直到下一個第2代集合(譯注:沒理解,不知道說的什麼)。

結束時,記得 一個垃圾回收器負責內存管理的托管環境的最大好處:內存洩漏,其它指針的服 務問題不在是你的問題。非內存資源迫使你要使用析構函數來確保清理非內存資 源。析構函數會對你的應用程序性能產生一些影響,但你必須使用它們來防止資 源洩漏(譯注:請注意理解非內存資源是什麼,一般是指文件句柄,網絡資源, 或者其它不能在內存中存放的資源)。通過實現IDisposable接口來避免析構函數 在垃圾回收器上造成的性能損失。接下來的具體的原則將會幫助你更有效的使用 環境來開發程序。

返回教程目錄

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved