|
上一篇文章介紹了.Net 垃圾回收的基本原理和垃圾回收執(zhí)行Finalize方法的內(nèi)部機(jī)制;這一篇我們看下弱引用對象,代,多線程垃圾回收,大對象處理以及和垃圾回收相關(guān)的性能計(jì)數(shù)器。
讓我們從弱引用對象說起,弱引用對象可以減輕大對象帶來的內(nèi)存壓力。
弱引用(Weak References)
當(dāng)程序的根對象指向一個對象時,這個對象是可達(dá)的,垃圾回收器不能回收它,這稱為對對象的強(qiáng)引用。和強(qiáng)引用相對的是弱引用,當(dāng)一個對象上存在弱引用時,垃圾回收器可以回收此對象,但是也允許程序訪問這個對象。這是怎么回事兒呢?請往下看。
如果一個對象上僅存在弱引用,并且垃圾回收器在運(yùn)行,這個對象就會被回收,之后如果程序中要訪問這個對象,訪問就會失敗。另一方面,要使用弱引用的對象,程序必須先對這個對象進(jìn)行強(qiáng)引用,如果程序在垃圾回收器回收這個對象之前對對象進(jìn)行了強(qiáng)引用,這樣(有了強(qiáng)引用之后)垃圾回收器就不能回收此對象了。這有點(diǎn)繞,讓我們用一段代碼來說明一下:
void Method() { //創(chuàng)建對象的強(qiáng)引用 Object o = new Object(); // 用一個短弱引用對象弱引用o. WeakReference wr = new WeakReference(o); o = null; // 移除對象的強(qiáng)引用 o = wr.Target; //嘗試從弱引用對象中獲得對象的強(qiáng)引用 if (o == null) { // 如果對象為空說明對象已經(jīng)被垃圾回收器回收掉了 } else { // 如果垃圾回收器還沒有回收此對象就可以繼續(xù)使用對象了 }
}
為什么需要弱對象呢?因?yàn)?,有一些?shù)據(jù)創(chuàng)建起來很容易,但是卻需要很多內(nèi)存。例如:你有一個程序,這個程序需要訪問用戶硬盤上的所有文件夾和文件名;你可以在程序第一次需要這個數(shù)據(jù)時訪問用戶磁盤生成一次數(shù)據(jù),數(shù)據(jù)生成之后你就可以訪問內(nèi)存中的數(shù)據(jù)來得到用戶文件數(shù)據(jù),而不是每次都去讀磁盤獲得數(shù)據(jù),這樣做可以提升程序的性能。 問題是這個數(shù)據(jù)可能相當(dāng)大,需要相當(dāng)大的內(nèi)存。如果用戶去操作程序的另外一部分功能了,這塊相當(dāng)大的內(nèi)存就沒有占用的必要了。你可以通過代碼刪除這些數(shù)據(jù),但是如果用戶馬上切換到需要這塊數(shù)據(jù)的功能上,你就必須重新從用戶的磁盤上構(gòu)建這個數(shù)據(jù)。弱引用為這種場景提供了一種簡單有效的方案。
當(dāng)用戶切換到其他功能時,你可以為這個數(shù)據(jù)創(chuàng)建一個弱引用對象,并把對這個數(shù)據(jù)的強(qiáng)引用解除掉。這樣如果程序占用的內(nèi)存很低,垃圾回收操作就不會觸發(fā),弱引用對象就不會被回收掉;這樣當(dāng)程序需要使用這塊數(shù)據(jù)時就可以通過一個強(qiáng)引用來獲得數(shù)據(jù),如果成功得到了對象引用,程序就沒有必要再次讀取用戶的磁盤了。
WeakReference類型提供了兩個構(gòu)造函數(shù):
WeakReference(object target); WeakReference(object target, bool trackResurrection);
target參數(shù)顯然就是弱引用要跟蹤的對象了。trackResurrection參數(shù)表示當(dāng)對象的Finalize方法執(zhí)行之后是否還要跟蹤這個對象。默認(rèn)這個參數(shù)是false。有關(guān)對象的復(fù)活請參考這里。 方便起見,不跟蹤復(fù)活對象的弱引用稱為“短弱引用”;而要跟蹤復(fù)活對象的的弱引用稱為“長弱引用”。如果對象沒有實(shí)現(xiàn)Finalize方法,那么長弱引用和短弱引用是完全一樣的。強(qiáng)烈建議你盡量避免使用長弱引用。長弱引用允許你使用復(fù)活的對象,而復(fù)活對象的行為可能是不可以預(yù)知的。
一旦你使用WeakReference引用了一個對象,建議你將這個對象的所有強(qiáng)用都設(shè)置為null;如果強(qiáng)引用存在的話,垃圾回收器是永遠(yuǎn)都不可能回收弱引用指向的對象的。
當(dāng)你要使用弱引用目標(biāo)對象時,你必須為目標(biāo)對象創(chuàng)建一個強(qiáng)引用,這很簡單,只要用object a = weekRefer.Target;就可以了,然后你必須判斷a是否為空,弱不為空才可以繼續(xù)使用,弱為空就表示對象已經(jīng)被垃圾回收器回收了,得通過其他方法重新獲得此對象。
弱引用的內(nèi)部實(shí)現(xiàn)
從前文中的描述中我們可以推斷出弱引用對象肯定和一般對象的處理是不一樣的。一般情況下如果一個對象引用了另一個對象就是強(qiáng)引用,垃圾回收器就不能回收被引用的對象,而WeakReference對象卻不是這樣子,它引用的對象是有可能被回收的。
要完全理解弱對象是如何工作的,我們還需要看一下托管堆。托管堆上有兩個內(nèi)部數(shù)據(jù)結(jié)構(gòu)他們的唯一作用是管理弱引用:我們可以把它們稱作長弱引用表和短弱引用表;這兩個表存放托管堆上的弱引用目標(biāo)對象指針。
程序運(yùn)行之初,這兩個表都是空的。當(dāng)你創(chuàng)建一個WeakReference對象時,這個對象并不是分配到托管堆上的,而是在弱對象表中創(chuàng)建一個空槽(Empty Slot)。短弱引用對象被放在短弱對象表中,長弱引用對象被放在長弱引用表中。
一旦發(fā)現(xiàn)空槽,空槽的值會被設(shè)置成弱引用目標(biāo)對象的地址;顯然長短弱對象表中的對象是不會當(dāng)作應(yīng)用程序的根對象的。垃圾回收器不會回收長短弱對象表中的數(shù)據(jù)。
讓我們來看下垃圾回收執(zhí)行時發(fā)生了什么:
1. 垃圾回收器構(gòu)建一個可達(dá)對象圖,構(gòu)建步驟請參考上文
2. 垃圾回收器掃描短弱對象表,如果弱對象表中指向的對像沒有在可達(dá)對象圖中,那么這個對像就被標(biāo)識為垃圾對象,然后短對象表中的對象指針被設(shè)置為空
3. 垃圾回收器掃描終結(jié)隊(duì)列(參考上文),如果隊(duì)列中的對象不在可達(dá)對象圖中,這個對象從終結(jié)隊(duì)列中移動到Freachable隊(duì)列中,這時候,這個對象又被標(biāo)識為可達(dá)對象,不再是垃圾了
4. 垃圾回收器掃描長弱引用表。如果表中的對象不在可達(dá)對象圖中(可達(dá)對象圖中包括在Freachable隊(duì)列中對象),將長引用對象表中對應(yīng)的對象指針設(shè)置為null
5. 垃圾回收器移動可達(dá)對象
一旦你理解了垃圾回收器的工作過程,就很容易理解弱引用是如何起作用了。訪問WeakReference的Target屬性導(dǎo)致系統(tǒng)返回弱對象表中的目標(biāo)對象指針,如果是null,表示對象已經(jīng)被回收了。
短弱引用不跟蹤復(fù)活,這意味著垃圾回收器可以在掃描終結(jié)隊(duì)列之前檢查弱引用表中指向的對象是否是垃圾對象。
而長弱引用跟蹤復(fù)活對象,這意味著垃圾回收器必須在確認(rèn)對象回收之后才可以將弱引用表中的指針設(shè)置為null。
代:
提起.Net的垃圾回收,c++或者c程序員可能就會想,這么管理內(nèi)存會不會出現(xiàn)性能問題呢。GC的開發(fā)人員一直在調(diào)整垃圾回收器提升它的性能。代就是一種為了降低垃圾回收對性能影響的機(jī)制。垃圾回收器在工作時會假定如下說法是成立的:
1. 一個對象越新,那么這個對象的生命周期就越短
2. 一個對象越老,那么這個對象的生命周期就越長
3. 新對象之間通常更可能和新對象之間存在引用關(guān)系
4. 壓縮堆的一部分要比壓縮整個堆要快
當(dāng)然大量研究證明以上幾個假設(shè)在很多程序上是成立的。那就讓我們來談?wù)勥@幾個假設(shè)是如何影響垃圾回收器工作的吧。
在程序初始化時,托管堆上沒有對象。這時候新添到托管堆上的對象是的代是0.如下圖所示,0代對象是最年輕的對象,他們從來沒有經(jīng)過垃圾回收器的檢查。
 圖1 托管堆上的0代對象
現(xiàn)在如果堆上添加了更多的對象,堆填滿時就會觸發(fā)垃圾回收。當(dāng)垃圾回收器分析托管堆時,會構(gòu)建一個垃圾對象(圖2中淺紫色塊)和非垃圾對象的圖。所有沒有被回收的對象會被移動壓縮到堆的最底端。這些沒有被回收掉的對象就成為了1代對象,如圖2所示
 圖2 托管堆上的0代1代對象
當(dāng)堆上分配了更多的對象時,新對象被放在了0代區(qū)。如果0代堆填滿了,就會觸發(fā)一次垃圾回收。這時候活下來的對象成為1代對象被移動到堆的底部;再此發(fā)生垃圾回收后1代對象中存活下來的對象會提升為2代對象并被移動壓縮。如圖3所示:
 圖3 托管堆上的0、1、2代對象
2代對象是目前垃圾回收器的最高代,當(dāng)再次垃圾回收時,沒有回收的對象的代數(shù)依然保持2.
垃圾回收分代為什么可以優(yōu)化性能
如前所述,分代回收可以提高性能。當(dāng)堆填滿之后會觸發(fā)垃圾回收,垃圾回收器可以只選擇0代上的對象進(jìn)行回收,而忽略更高代堆上的對象。然而,由于越年輕的對象生命周期越短,因此,回收0代堆可以回收相當(dāng)多的內(nèi)存,而且回收所耗的性能也比回收所有代對象要少得多。
這是分代垃圾回收的最簡單優(yōu)化。分代回收不需要便利整個托管堆,如果一個根對象引用了一個高代對象,那么垃圾回收器可以忽略高代對象和其引用對象的遍歷,這會大大減少構(gòu)建可達(dá)對象圖的時間。
如果回收0代對象沒有釋放出足夠的內(nèi)存,垃圾回收器會嘗試回收1代和0代堆;如果仍然沒有獲得足夠的內(nèi)存,那么垃圾回收器會嘗試回收2,1,0代堆。具體會回收那一代對象的算法不是確定的,微軟會持續(xù)做算法優(yōu)化。
多數(shù)堆(像c-runtime堆)只要找到足夠的空閑內(nèi)存就分配給對象。因此,如果我連續(xù)分配多個對象時,這些對象的地址空間可能會相差幾M。然而在托管堆上,連續(xù)分配的對象的內(nèi)存地址是連續(xù)的。
前面的假設(shè)中還提到,新對象之間更可能存在相互引用關(guān)系。因此新對象分配到連續(xù)的內(nèi)存上,你可以獲得就近引用的性能優(yōu)化(you gain performance from locality of reference)。這樣的話很可能你的對象都在CPU的緩存中,這樣CPU的很多操作就不需要去存取內(nèi)存了。
微軟的性能測試顯示托管堆的分配速度比標(biāo)準(zhǔn)的win32 HeapAlloc方法還要快。這些測試也顯示了200MHz的Pentium的CPU做一次0代回收時間可以小于1毫秒。微軟的優(yōu)化目的是讓垃圾回收耗用的時間小于一次普通的頁面錯誤。
使用System.GC類控制垃圾回收
類型System.GC運(yùn)行開發(fā)人員直接控制垃圾回收器。你可以通過GC.MaxGeneration屬性獲得GC的最高代數(shù),目前最高代是定值2.
你可以調(diào)用GC.Collect()方法強(qiáng)制垃圾回收器做垃圾回收,Collect方法有兩個重載:
void GC.Collect(Int32 generation)
void GC.Collect()
第一個方法允許你指定要回收那一代。你可以傳0到GC.MaxGeneration的數(shù)字做參數(shù),傳0只做0代堆的回收,傳1會回收1代和0代堆,而傳2會回收整個托管堆。而無參數(shù)的方法調(diào)用GC.Collect(GC.MaxGeneration)相當(dāng)于整個回收。 在通常情況下,不應(yīng)該去調(diào)用GC.Collect方法;最好讓垃圾回收器按照自己的算法判斷什么時候該調(diào)用Collect方法。盡管如此,如果你確信比運(yùn)行時更了解什么時候該做垃圾回收,你就可以調(diào)用Collect方法去做回收。比如說程序可以在保存數(shù)據(jù)文件之后做一次垃圾回收。比如你的程序剛剛用完一個長度為10000的大數(shù)組,你不再需要他了,就可以把它設(shè)置為null然后執(zhí)行垃圾回收,緩解內(nèi)存的壓力。
GC還提供了WaitForPendingFinalizers方法。這個方法簡單的掛起執(zhí)行線程,知道Freachable隊(duì)列中的清空之后,執(zhí)行完所有隊(duì)列中的Finalize方法之后才繼續(xù)執(zhí)行。
GC還提供了兩個方法用來返回某個對象是幾代對象,他們是
Int32 GC.GetGeneration(object o);
Int32 GC.GetGeneration(WeakReference wr)
第一個方法返回普通對象是幾代,第二個方法返回弱引用對象的代數(shù)。
下面的代碼可以幫助你理解代的意義:
private static void GenerationDemo() { // Let's see how many generations the GCH supports (we know it's 2) Display('Maximum GC generations: ' + GC.MaxGeneration); // Create a new BaseObj in the heap GenObj obj = new GenObj('Generation'); // Since this object is newly created, it should be in generation 0 obj.DisplayGeneration(); // Displays 0 // Performing a garbage collection promotes the object's generation GC.Collect(); obj.DisplayGeneration(); // Displays 1 GC.Collect(); obj.DisplayGeneration(); // Displays 2 GC.Collect(); obj.DisplayGeneration(); // Displays 2 (max generation) obj = null; // Destroy the strong reference to this object GC.Collect(0); // Collect objects in generation 0 GC.WaitForPendingFinalizers(); // We should see nothing GC.Collect(1); // Collect objects in generation 1 GC.WaitForPendingFinalizers(); // We should see nothing GC.Collect(2); // Same as Collect() GC.WaitForPendingFinalizers(); // Now, we should see the Finalize // method run Display(-1, 'Demo stop: Understanding Generations.', 0); } class GenObj{ public void DisplayGeneration(){ Console.WriteLine(“my generation is ” + GC.GetGeneration(this)); } ~GenObj(){ Console.WriteLine(“My Finalize method called”); }
}
垃圾回收機(jī)制的多線程性能優(yōu)化 在前面的部分,我解釋了GC的算法和優(yōu)化,然后討論的前提都是在單線程情況下的。而在真實(shí)的程序中,很可能是多個線程一起工作,多個線程一起操縱托管堆上的對象。當(dāng)一個線程觸發(fā)了垃圾回收,其他所有的線程都應(yīng)該暫停訪問任何引用對象(包括他們自己棧上引用的對象),因?yàn)槔厥掌饔锌赡芤苿訉ο?,修改對象的?nèi)存地址。
因此當(dāng)垃圾回收器開始回收時,所有執(zhí)行托管代碼的線程必須掛起。運(yùn)行時有幾種不同的機(jī)制可以安全的掛起線程來執(zhí)行垃圾回收。這一塊的內(nèi)部機(jī)制我不打算詳細(xì)說明。但是微軟會持續(xù)修改垃圾回收的機(jī)制來降低垃圾回收帶來的性能損耗。
下面幾段描述了垃圾回收器在多線程情況下是如何工作的: 完全中斷代碼執(zhí)行 當(dāng)垃圾回收開始執(zhí)行時,掛起所有應(yīng)用程序線程。垃圾回收器隨后將線程掛起的位置記錄到一個just-in-time(JIT)編譯器生成的表中,垃圾回收器負(fù)責(zé)將線程掛起的位置記錄在表中,記錄當(dāng)前正在訪問的對象,以及對象存放的位置(變量中,CPU寄存器中,等等) 劫持:垃圾回收器可以修改線程的棧讓返回地址指向一個特殊的方法,當(dāng)當(dāng)前執(zhí)行的方法返回時,這個特殊的方法將會執(zhí)行,掛起線程,這種改變線程執(zhí)行路徑的方式稱為劫持線程。當(dāng)垃圾回收完成之后,線程會重新返回到之前執(zhí)行的方法上。
安全點(diǎn): 當(dāng)JIT編譯器編譯一個方法時,可以在某個點(diǎn)插入一段代碼判斷GC是否掛起,如果是,線程就掛起等待垃圾回收完成,然后線程重新開始執(zhí)行。JIT編譯器插入檢查GC代碼的位置被稱作“安全點(diǎn)”
請注意,線程劫持允許正在執(zhí)行非托管代碼的線程在垃圾回收過程中執(zhí)行。如果非托管代碼不訪問托管堆上的對象時這是沒有問題的。如果這個線程當(dāng)前執(zhí)行非托管代碼然后返回執(zhí)行托管代碼,這個線程將會被劫持,直到垃圾回收完成之后再繼續(xù)執(zhí)行。
除了我剛提到的集中機(jī)制之外,垃圾回收器還有其他改進(jìn)來增強(qiáng)多線程程序中的對象內(nèi)存分配和回收。
同步釋放分配(Synchronization-free Allocations):在一個多線程系統(tǒng)中,0代堆被分成幾個區(qū)域,一個線程使用一個區(qū)域。這允許多線程同時分配對象,并不需要一個線程獨(dú)占堆。
可伸縮回收(Scalable Collections):在多線程系統(tǒng)中運(yùn)行執(zhí)行引擎的服務(wù)器版本(MXSorSvr.dll).托管堆會被分成幾個不同的區(qū)域,一個CPU一個區(qū)域。當(dāng)回收初始化時,每個CPU執(zhí)行一個回收線程,各個線程回收各自的區(qū)域。而工作站版本的執(zhí)行引擎(MXCorWks.dll)不支持這個功能。
大對象回收
這一塊就不翻譯了,有一篇專門的文章談這件事兒
監(jiān)視垃圾回收
如果你安裝了.Net framework你的性能計(jì)數(shù)器(開始菜單—管理工具—性能 進(jìn)入)中就會有.Net CLR Memory一項(xiàng),你可以從實(shí)例列表中選擇某個程序進(jìn)行觀察,如下圖所示。

這些性能指標(biāo)的具體含義如下:
性能計(jì)數(shù)器 | 說明 | # Bytes in all Heaps(所有堆中的字節(jié)數(shù)) | 顯示以下計(jì)數(shù)器值的總和:“第 0 級堆大小”計(jì)數(shù)器、“第 1 級堆大小”計(jì)數(shù)器、“第 2 級堆大小”計(jì)數(shù)器和“大對象堆大小”計(jì)數(shù)器。此計(jì)數(shù)器指示在垃圾回收堆上分配的當(dāng)前內(nèi)存(以字節(jié)為單位)。 | # GC Handles(GC 處理數(shù)目) | 顯示正在使用的垃圾回收處理的當(dāng)前數(shù)目。垃圾回收處理是對公共語言運(yùn)行庫和托管環(huán)境外部的資源的處理。 | # Gen 0 Collections(第 2 級回收次數(shù)) | 顯示自應(yīng)用程序啟動后第 0 級對象(即最年輕、最近分配的對象)被垃圾回收的次數(shù)。 當(dāng)?shù)?0 級中的可用內(nèi)存不足以滿足分配請求時發(fā)生第 0 級垃圾回收。此計(jì)數(shù)器在第 0 級垃圾回收結(jié)束時遞增。較高級的垃圾回收包括所有較低級的垃圾回收。當(dāng)較高級(第 1 級或第 2 級)垃圾回收發(fā)生時此計(jì)數(shù)器被顯式遞增。 此計(jì)數(shù)器顯示最近的觀察所得值。_Global_ 計(jì)數(shù)器值不準(zhǔn)確,應(yīng)該忽略。 | # Gen 1 Collections(第 2 級回收次數(shù)) | 顯示自應(yīng)用程序啟動后對第 1 級對象進(jìn)行垃圾回收的次數(shù)。 此計(jì)數(shù)器在第 1 級垃圾回收結(jié)束時遞增。較高級的垃圾回收包括所有較低級的垃圾回收。當(dāng)較高級(第 2 級)垃圾回收發(fā)生時此計(jì)數(shù)器被顯式遞增。 此計(jì)數(shù)器顯示最近的觀察所得值。_Global_ 計(jì)數(shù)器值不準(zhǔn)確,應(yīng)該忽略。 | # Gen 2 Collections(第 2 級回收次數(shù)) | 顯示自應(yīng)用程序啟動后對第 2 級對象進(jìn)行垃圾回收的次數(shù)。此計(jì)數(shù)器在第 2 級垃圾回收(也稱作完整垃圾回收)結(jié)束時遞增。 此計(jì)數(shù)器顯示最近的觀察所得值。_Global_ 計(jì)數(shù)器值不準(zhǔn)確,應(yīng)該忽略。 | # Induced GC(引發(fā)的 GC 的數(shù)目) | 顯示由于對 GC.Collect 的顯式調(diào)用而執(zhí)行的垃圾回收的峰值次數(shù)。讓垃圾回收器對其回收的頻率進(jìn)行微調(diào)是切實(shí)可行的。 | # of Pinned Objects(釘住的對象的數(shù)目) | 顯示上次垃圾回收中遇到的釘住的對象的數(shù)目。釘住的對象是垃圾回收器不能移入內(nèi)存的對象。此計(jì)數(shù)器只跟蹤被進(jìn)行垃圾回收的堆中的釘住的對象。例如,第 0 級垃圾回收導(dǎo)致僅枚舉第 0 級堆中釘住的對象。 | # of Sink Blocks in use(正在使用的接收塊的數(shù)目) | 顯示正在使用的同步塊的當(dāng)前數(shù)目。同步塊是為存儲同步信息分配的基于對象的數(shù)據(jù)結(jié)構(gòu)。同步塊保留對托管對象的弱引用并且必須由垃圾回收器掃描。同步塊不局限于只存儲同步信息;它們還可以存儲 COM interop 元數(shù)據(jù)。該計(jì)數(shù)器指示與同步基元的過度使用有關(guān)的性能問題。 | # Total committed Bytes(提交字節(jié)的總數(shù)) | 顯示垃圾回收器當(dāng)前提交的虛擬內(nèi)存量(以字節(jié)為單位)。提交的內(nèi)存是在磁盤頁面文件中保留的空間的物理內(nèi)存。 | # Total reserved Bytes(保留字節(jié)的總數(shù)) | 顯示垃圾回收器當(dāng)前保留的虛擬內(nèi)存量(以字節(jié)為單位)。保留內(nèi)存是為應(yīng)用程序保留(但尚未使用任何磁盤或主內(nèi)存頁)的虛擬內(nèi)存空間。 | % Time in GC(GC 中時間的百分比) | 顯示自上次垃圾回收周期后執(zhí)行垃圾回收所用運(yùn)行時間的百分比。此計(jì)數(shù)器通常指示垃圾回收器代表該應(yīng)用程序?yàn)槭占蛪嚎s內(nèi)存而執(zhí)行的工作。只在每次垃圾回收結(jié)束時更新此計(jì)數(shù)器。此計(jì)數(shù)器不是一個平均值;它的值反映了最近觀察所得值。 | Allocated Bytes/second(每秒分配的字節(jié)數(shù)) | 顯示每秒在垃圾回收堆上分配的字節(jié)數(shù)。此計(jì)數(shù)器在每次垃圾回收結(jié)束時(而不是在每次分配時)進(jìn)行更新。此計(jì)數(shù)器不是一段時間內(nèi)的平均值;它顯示最近兩個樣本中觀測的值的差除以取樣間隔時間所得的結(jié)果。 | Finalization Survivors(完成時存留對象數(shù)目) | 顯示因正等待完成而從回收后保留下來的進(jìn)行垃圾回收的對象的數(shù)目。如果這些對象保留對其他對象的引用,則那些對象也保留下來,但此計(jì)數(shù)器不對它們計(jì)數(shù)。“從第 0 級提升的完成內(nèi)存”和“從第 1 級提升的完成內(nèi)存”計(jì)數(shù)器表示因完成而保留下來的所有內(nèi)存。 此計(jì)數(shù)器不是累積計(jì)數(shù)器;它在每次垃圾回收結(jié)束時由僅在該特定回收期間存留對象的計(jì)數(shù)更新。此計(jì)數(shù)器指示由于完成應(yīng)用程序可能導(dǎo)致系統(tǒng)開銷過高。 | Gen 0 heap size(第 2 級堆大?。?/strong> | 顯示在第 0 級中可以分配的最大字節(jié)數(shù);它不指示在第 0 級中當(dāng)前分配的字節(jié)數(shù)。 當(dāng)自最近回收后的分配超出此大小時發(fā)生第 0 級垃圾回收。第 0 級大小由垃圾回收器進(jìn)行微調(diào)并且可在應(yīng)用程序執(zhí)行期間更改。在第 0 級回收結(jié)束時,第 0 級堆的大小是 0 字節(jié)。此計(jì)數(shù)器顯示調(diào)用下一個第 0 級垃圾回收的分配的大?。ㄒ宰止?jié)為單位)。 此計(jì)數(shù)器在垃圾回收結(jié)束時(而不是在每次分配時)進(jìn)行更新。 | Gen 0 Promoted Bytes/Sec(從第 1 級提升的字節(jié)數(shù)/秒) | 顯示每秒從第 0 級提升到第 1 級的字節(jié)數(shù)。內(nèi)存在從垃圾回收保留下來后被提升。此計(jì)數(shù)器是每秒創(chuàng)建的在相當(dāng)長時間保留下來的對象的指示符。 此計(jì)數(shù)器顯示在最后兩個樣本(以取樣間隔持續(xù)時間來劃分)中觀察到的值之間的差異。 | Gen 1 heap size(第 2 級堆大?。?/strong> | 顯示第 1 級中的當(dāng)前字節(jié)數(shù);此計(jì)數(shù)器不顯示第 1 級的最大大小。不直接在此代中分配對象;這些對象是從前面的第 0 級垃圾回收提升的。此計(jì)數(shù)器在垃圾回收結(jié)束時(而不是在每次分配時)進(jìn)行更新。 | Gen 1 Promoted Bytes/Sec(從第 1 級提升的字節(jié)數(shù)/秒) | 顯示每秒從第 1 級提升到第 2 級的字節(jié)數(shù)。在此計(jì)數(shù)器中不包括只因正等待完成而被提升的對象。 內(nèi)存在從垃圾回收保留下來后被提升。不會從第 2 級進(jìn)行任何提升,因?yàn)樗亲钆f的一級。此計(jì)數(shù)器是每秒創(chuàng)建的非常長時間保留下來的對象的指示符。 此計(jì)數(shù)器顯示在最后兩個樣本(以取樣間隔持續(xù)時間來劃分)中觀察到的值之間的差異。 | Gen 2 heap size(第 2 級堆大小) | 顯示第 2 級中當(dāng)前字節(jié)數(shù)。不直接在此代中分配對象;這些對象是在以前的第 1 級垃圾回收期間從第 1 級提升的。此計(jì)數(shù)器在垃圾回收結(jié)束時(而不是在每次分配時)進(jìn)行更新。 | Large Object Heap size(大對象堆大?。?/strong> | 顯示大對象堆的當(dāng)前大?。ㄒ宰止?jié)為單位)。垃圾回收器將大于 20 KB 的對象視作大對象并且直接在特殊堆中分配大對象;它們不是通過這些級別提升的。此計(jì)數(shù)器在垃圾回收結(jié)束時(而不是在每次分配時)進(jìn)行更新。 | Promoted Finalization-Memory from Gen 0(從第 1 級提升的完成內(nèi)存) | 顯示只因等待完成而從第 0 級提升到第 1 級的內(nèi)存的字節(jié)數(shù)。此計(jì)數(shù)器不是累積計(jì)數(shù)器;它顯示在最后一次垃圾回收結(jié)束時觀察到的值。 | Promoted Finalization-Memory from Gen 1(從第 1 級提升的完成內(nèi)存) | 顯示只因等待完成而從第 1 級提升到第 2 級的內(nèi)存的字節(jié)數(shù)。此計(jì)數(shù)器不是累積計(jì)數(shù)器;它顯示在最后一次垃圾回收結(jié)束時觀察到的值。如果最后一次垃圾回收就是第 0 級回收,此計(jì)數(shù)器則重置為 0。 | Promoted Memory from Gen 0(從第 1 級提升的內(nèi)存) | 顯示在垃圾回收后保留下來并且從第 0 級提升到第 1 級的內(nèi)存的字節(jié)數(shù)。此計(jì)數(shù)器中不包括那些只因等待完成而提升的對象。此計(jì)數(shù)器不是累積計(jì)數(shù)器;它顯示在最后一次垃圾回收結(jié)束時觀察到的值。 | Promoted Memory from Gen 1(從第 1 級提升的內(nèi)存) | 顯示在垃圾回收后保留下來并且從第 1 級提升到第 2 級的內(nèi)存的字節(jié)數(shù)。此計(jì)數(shù)器中不包括那些只因等待完成而提升的對象。此計(jì)數(shù)器不是累積計(jì)數(shù)器;它顯示在最后一次垃圾回收結(jié)束時觀察到的值。如果最后一次垃圾回收就是第 0 級回收,此計(jì)數(shù)器則重置為 0。 |
這個表來自MSDN
英文原文:Jeffrey Richter 編譯:趙玉開 鏈接http://www.cnblogs.com/yukaizhao/archive/2011/11/25/dot_net_GC_2.html
|