天天看點

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

 關于.Net垃圾收集器(Garbage Collection),Aicken已經在“.Net Discovery 系列”文章中有2篇的涉及,這一篇文章是對上2篇文章的補充,關于“.Net Discovery 系列”文章索引請見本文結尾。

    第一節.垃圾回收算法與完整收集(Full GC)

    垃圾收集器就是跟蹤所有被引用到的對象,整理對象不再被引用的對象,回收相應的記憶體,它使用“标記與清除”算法,分兩步回收對象:

    Step 1.Mark-Sweep :從應用程式的root出發,利用互相引用關系,周遊其在Heap上動态配置設定的所有對象,指明需要回收的對象,标記出那些存活的對象,予以标記。

    Step 2.Compact: 對記憶體中存活的對象進行移動,修改它們的指針,使之在記憶體中連續,這樣空閑的記憶體也就連續了,即完成了記憶體釋放工作,也解決了記憶體碎片問題,這個過程也可以成為指針的壓縮。

    垃圾收集器一般将托管堆中的對象分為3代,這可以通過調用GC.MaxGeneration得知,對象按照存在時間長短進行分代,最短的分在第0代,最長的分在第2代,第2代中的對象往往是比較大的,第二代空間被稱作Large Object Heap,對于2代對象的回收,與第0、1代回收方式相比最大的不同在于,沒有了指針移動的壓縮過程。

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

                                  圖1 對象的回收

    如上圖所示,左邊的區域為第一次GC時的結構,需要注意的是GC标記的是那些存活的對象,而不是需要回收的,是以第一次回收,對象B、D沒有被标記,是以被回收了,之後GC移動了對象記憶體指針,使空間連續。

    接下來看中間的部分,第二次GC開始了,C對象沒有被标記,是以被回收了,接下來A、D、F三個對象被壓縮,形成連續的記憶體空間,并且形成了第1、2、3代區域。

    接下來看最右邊的部分,D對象沒有被标記,由于D對象處于第2代中,是以回收D對象後,GC沒有啟動壓縮步驟,因為對于大對象的指針移動,資源耗費成本很高。

    對于第2代的GC稱為Full GC,新配置設定的對象在第0代(0代空間最大長度通常為256K),按位址順序配置設定,它們通常是一些局部變量;第1代(1代空間最大長度通常為2 MB)是經過0代垃圾收集後仍然駐留在記憶體中的對象,它們通常是一些如表單,按鈕等對象;第2代是經曆過幾次垃圾收集後仍然駐留在記憶體中的對象,它們通常是一些應用程式對象。

    可見一次Full GC需要的資源是最多的,可能是幾秒或十幾秒。

    托管堆的記憶體配置設定以段(Segment)為機關,CLR啟動時通常為GC Heap建立2個段,分别用來存儲第0、1代對象和第2代對象,以下是通過Windbg工具檢視到的GC Heap情況:

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

圖2 WinDbg 檢視GC Heap情況

    可以看出,GC堆被分成了兩個段,三代,每代起始位址十進制內插補點為12。

    在了解方面需要注意的是,GC回收的是程式中的引用類型,值類型是儲存在堆棧之中,當值類型對象出了作用域後會自動釋放記憶體----即彈棧,不需要垃圾收集器管理。

<b>    第二節.GC</b><b>的工作模式</b>

    GC的工作模式分3種,Workstation GC with Concurrent GC off、 Workstation GC with Concurrent GC on、Server GC ,在.Net 2.0以上版本可以通過修改Config檔案來改變GC工作模式,例如啟用Server GC:

&lt;configuration&gt;

&lt;runtime&gt;

&lt;gcServer enabled="true" /&gt;

&lt;/runtime&gt;

&lt;/configuration&gt;

    或者通過.Net配置工具,檢視“我的電腦”節點屬性可以友善的改變GC工作模式,如下圖:

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

圖3 GC工作模式

    <b>Workstation GC without Concurrent: </b>用于單CPU的伺服器,政策引擎會調節GC工作頻率,使用挂起-&gt;查找與标記-&gt;壓縮-&gt;恢複的流程進行GC工作。

    <b>Workstation GC with Concurrent:</b> Concurrent GC與Non Concurrent GC模式相比,有着更靈活的反應速度,Winform應用程式和Windows services 服務程式預設采用這種模式,單CPU機器上隻能使用workstation GC方式,預設為 Workstation GC with Concurrent。

    在這種模式下,第0、1代的收集仍然是要暫時挂起應用程式的,隻有在收集第2代時,才會并行處理,這種并行收集是利用多CPU

對Full GC進行并行處理,具體原理是将Full GC過程切分成多個短暫子過程對線程進行當機,線上程當機時間之外,應用程式仍然可

以正常運作。這主要通過将0代空間設定的很大,使Full GC時,CLR仍然能夠在0代中進行記憶體配置設定,如果Full GC時0代記憶體也已用盡,那麼應用程式将被挂起,等待Full GC的完成。

    <b>Server GC</b>: 用于多CPU的伺服器,這種GC模式有着很高的性能和效率。這種模式下,CLR為每個CPU建立一個專用的GC線程,每個CPU可以獨立的為相應的heap執行GC操作,這些GC線程是以非并發的形式工作的,收集工作與線程正常工作不能同時進行,這就是說第0、1、2代的收集都會挂起應用線程。

    在.Net 4.0中,有一種新的垃圾收集機制,叫做背景收集。這種機制以concurrent GC為基礎的,如上文所講,Workstation GC with Concurrent模式中,在Full GC過程時,CLR仍然能夠在0代中進行記憶體配置設定,如果Full GC時0代記憶體也已用盡,那麼應用程式将被挂起,等待Full GC的完成。

    這個過程在背景收集機制中是這樣工作的,在進行Full GC時可以同時進行第0、1代收集,并且背景收集是一個獨立線程完成的,這個程序任務優先級低于第0、1代收集,如果在背景收集中需要對第0、1代收集,背景收集将會等待第0、1代收集完成後再進行工

作,當然第0、1代收集是需要短暫挂起應用的。

    背景收集還會根據政策引擎的訓示,動态調節第0、1代的容量,減少前台收集(第0、1代收集)次數。

<b>    第三節 .Net 4.0</b><b>中的垃圾收集器</b>

    在.Net 3.5 SP1中,FrameWork中新增了如下方法,并且在4.0中進行了優化,GC.RegisterForFullGCNotification 、GC.WaitForFullGCApproach 、GC.WaitForFullGCComplete 、GC.CancelFullGCNotification,這幾個方法都是針對Full GC(完整收集)的。

int maxGenerationThreshold

int largeObjectHeapThreshold

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒
.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

代碼

// Variable for continual checking in the

// While loop in the WaitForFullGCProc method.

static bool checkForNotify = false;

// Variable for suspending work

// (such servicing allocated server requests)

// after a notification is received and then

// resuming allocation after inducing a garbage collection.

static bool bAllocate = false;

// Variable for ending the example.

static bool finalExit = false;

// Collection for objects that

// simulate the server request workload.

static List&lt;byte[]&gt; load = new List&lt;byte[]&gt;();

public static void Main(string[] args)

{

try

// Register for a notification.

GC.RegisterForFullGCNotification(10, 10);

Console.WriteLine("Registered for GC notification.");

checkForNotify = true;

bAllocate = true;

// Start a thread using WaitForFullGCProc.

Thread thWaitForFullGC = new Thread(new ThreadStart(WaitForFullGCProc));

thWaitForFullGC.Start();

// While the thread is checking for notifications in

// WaitForFullGCProc, create objects to simulate a server workload.

int lastCollCount = 0;

int newCollCount = 0;

while (true)

if (bAllocate)

load.Add(new byte[1000]);

newCollCount = GC.CollectionCount(2);

if (newCollCount != lastCollCount)

// Show collection count when it increases:

Console.WriteLine("Gen 2 collection count: {0}", GC.CollectionCount(2).ToString());

lastCollCount = newCollCount;

}

// For ending the example (arbitrary).

if (newCollCount == 500)

finalExit = true;

checkForNotify = false;

break;

catch (OutOfMemoryException)

Console.WriteLine("Out of memory.");

GC.CancelFullGCNotification();

catch (InvalidOperationException invalidOp)

Console.WriteLine("GC Notifications are not supported while concurrent GC is enabled.\n"

+ invalidOp.Message);

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

 <b>3.GC.WaitForFullGCApproach:</b>用來獲得垃圾收集器是否将要啟動完整垃圾收集的工作,該方法傳回GCNotificationStatus枚舉值,當枚舉為Succeeded時,

<b></b>

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒
.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

<b>// 檢視是否将啟動完整收集</b>

GCNotificationStatus s = GC.WaitForFullGCApproach();

if (s == GCNotificationStatus.Succeeded)

//do not GC.Collect()

else if (s == GCNotificationStatus.Canceled)

// GC.Collect()

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

<b> </b>

.Net Discovery 系列之七--深入了解.Net垃圾收集機制(拾貝篇) 釋出在新年第一秒

我是李鳴(Aicken) 請您繼續關注我的下一篇文章。

    “.Net Discovery 系列”推薦:

<b>本文轉自Aicken(李鳴)部落格園部落格,原文連結:http://www.cnblogs.com/isline/archive/2010/01/01/1637241.html,如需轉載請自行聯系原作者</b>