程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 理解垃圾回收平台的基本工作原理,垃圾回收工作原理

理解垃圾回收平台的基本工作原理,垃圾回收工作原理

編輯:C#入門知識

理解垃圾回收平台的基本工作原理,垃圾回收工作原理


理解垃圾回收平台的基本工作原理

每個程序都有使用不同的資源,比如文件、內存緩沖、屏幕空間、網絡連接、數據庫連接等,其實在面上對象的程序設計中,每個類型都代表可供程序使用的一種資源,

要使用這些資源就必須為這些資源類型分配內存。下面是訪問一種資源所需的步驟:

(1)  調用IL指令的newobj,為代表資源的類型分配內存,在c#中使用new操作符,編譯器就會自動生成該指令。

(2)  初始化資源,設置資源的初始狀態,使資源可用。類型的實例構造器負責這個工作。

(3)  訪問類型成員來使用資源。

(4)  摧毀資源狀態以進行清理。

(5)  釋放內存。垃圾回收器負責這一步。

看是如此簡單的過程,有多少程序員忘記釋放不再需要的內存?又有多少程序員試圖調用已釋放的內存?

進行非托管編程時,應用程序的這兩種bug比其它bug嚴重很多,一般都無法預測到他們的後果,如果是其它bug,可以發現程序行為異常來修正。但是這兩種bug會造成資源洩露和對象損壞,是應用程序表現不可預測。

通常進行資源管理很難而且很枯燥。它會極大分散開發人員的注意力,使之無法專注要真正解決的問題。要是有一種機制能為開發人員簡化這個工作,那是一件不錯的事情,幸好有這中機制,這就是垃圾回收(簡稱GC –garbage collection).

有了GC使的開發人員的到解放,就不必跟蹤內存的使用,也不必知道什麼時候釋放內存。但是垃圾回收對內存中的類型資源一無所知。這意味著GC不知道怎麼執行前面的第四步:摧毀資源狀態以進行清理 。為了使資源得到正確的清理,開發人員必須編寫知道如何正確清理資源的代碼。這些代碼通常放在Finalize,Dispose和close方法中,但是GC提供了一些幫助,使得開發人員大多數時候可以跳過第四步。

 

另外要注意,值類型、集合類型、String、Attribute、Delegate和Exception所代表的資源無需進行特殊清理。

如果一個類型代表非托管和本地資源(比如文件、數據庫連接、套接字、mutex、位圖和圖標等),那麼在對象准備回收時,必須進行一些清理代碼。

2、從托管堆中分配資源

CLR要求所有托管代碼的資源在托管堆中分配。這個堆和C運行時的堆非常相似,只是你永遠不要從托管堆中刪除對象(應用程序不需要的對象會自動刪除),這就引出一個問題:托管堆如何知道應用程序不需要一個對象?

目前有好多種垃圾回收器的算法,不同算法都針對不同環境進行優化,已提供這種環境下最佳的性能。本系列將關注Microsoft.NET Framework的CLR所采用的垃圾回收算法。

前面說到new負責創建一個對象,導致編譯器生成一個newobj指令。該指令會導致CLR執行以下步驟。

(1)  計算類型(以及所有基類型)資源所需要的字節數。

(2)  加上對象開銷所需的字節數。每個對象都有兩個字節的開銷:一個是類型對象指針和一個同步塊索引(可能不同資料上面名字翻譯不一樣,以後章節中再對這兩種不同的開銷做講解,你也可以去MSDN網站上查一些資料),真對32位應用程序和64位應用程序分配的字節是不一樣的。

(3)  CLR檢查保留區域是否能提供分配對象所需的字節數,如果托管堆有足夠的空間對象會被放進去,這裡有個NextObjPtr指針,該對象是在這個指針指向的地址放入的。並且為他分配的字節數就會清零,接著調用類型實例構造器,IL指令newobj會返回對象的地址。在返回地址之前,NextObjPtr指針的值會加上對象占據的字節數,這樣會得到一個新值,他指向下一個對象放入托管堆得地址。

                           

                

為了對比,讓我們看一下C運行時堆如何分配。在C運行時堆中,為對象分配內存要遍歷一個數據結構組成的鏈表。一旦發現有足夠大的塊,那個塊就會被拆分,同時修改鏈表節點的指針,以確保鏈表的完整性。對於托管堆,分配對象只需要在指針上加一個值,這個顯然明顯快得多。事實上,托管堆分配對象的速度幾乎可以與從線程棧分配內存媲美。另外,大多數堆(包含C運行時堆)都是在他們找到可用空間的地方分配,所以連續創建幾個對象,有可能被分散。中間相隔數MB的地址空間。但是在托管堆中連續分配對象能夠確保他們對象的地址是連續的。

綜合上訴,似乎托管堆得實現的簡單行和速度方面遠遠優於C運行時堆。但是這有個特別注意的細節你的搞清楚,托管堆之所以有這麼多好處,是因為它做了一個相當大膽的假設:“地址空間和存儲是無限的”。這個假設簡直荒謬之極。所以托管堆必須通過某種機制來允許它這麼假設。這種機制就是垃圾回收。

應用程序調用new創建對象時,可能沒有足夠的地址空間來分配該對象,托管堆將需要的字節加到NextObjPtr指針中的地址上來檢查這種情況。如果結果值超過了地址的末尾,表明托管堆已滿,必須進行垃圾回收了。

 備注:本系列文章來自<<CLR via C#>>第三版

 

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