.Net 垃圾回收机制原理(一)
英文原文:Jeffrey Richter
編譯:趙玉開(kāi)
鏈接:http://www.cnblogs.com/yukaizhao/archive/2011/11/23/dot_net_GC_1.html
有了Microsoft.Net clr中的垃圾回收機(jī)制程序員不需要再關(guān)注什么時(shí)候釋放內(nèi)存,釋放內(nèi)存這件事兒完全由GC做了,對(duì)程序員來(lái)說(shuō)是透明的。盡管如此,作為一個(gè).Net程序員很有必要理解垃圾回收是如何工作的。這篇文章我們就來(lái)看下.Net是如何分配和管理托管內(nèi)存的,之后再一步一步描述垃圾回收器工作的算法機(jī)制。
為程序設(shè)計(jì)一個(gè)適當(dāng)?shù)膬?nèi)存管理策略是困難的也是乏味的,這個(gè)工作還會(huì)影響你專注于解決程序本身要解決的問(wèn)題。有沒(méi)有一種內(nèi)置的方法可以幫助開(kāi)發(fā)人員解決內(nèi)存管理的問(wèn)題呢?當(dāng)然有了,在.Net中就是GC,垃圾回收。
讓我們想一下,每一個(gè)程序都要使用內(nèi)存資源:例如屏幕顯示,網(wǎng)絡(luò)連接,數(shù)據(jù)庫(kù)資源等等。實(shí)際上,在一個(gè)面向?qū)ο蟓h(huán)境中,每一種類型都需要占用一點(diǎn)內(nèi)存資源來(lái)存放他的數(shù)據(jù),對(duì)象需要按照如下的步驟使用內(nèi)存:
1. 為類型分配內(nèi)存空間
2. 初始化內(nèi)存,將內(nèi)存設(shè)置為可用狀態(tài)
3. 存取對(duì)象的成員
4. 銷毀對(duì)象,使內(nèi)存變成清空狀態(tài)
5. 釋放內(nèi)存
這種貌似簡(jiǎn)單的內(nèi)存使用模式導(dǎo)致過(guò)很多的程序問(wèn)題,有時(shí)候程序員可能會(huì)忘記釋放不再使用的對(duì)象,有時(shí)候又會(huì)試圖訪問(wèn)已經(jīng)釋放的對(duì)象。這兩種bug通常都有一定的隱藏性,不容易發(fā)現(xiàn),他們不像邏輯錯(cuò)誤,發(fā)現(xiàn)了就可以修改掉。他們可能會(huì)在程序運(yùn)行一段時(shí)間之后內(nèi)存泄漏導(dǎo)致意外的崩潰。事實(shí)上,有很多工具可以幫助開(kāi)發(fā)人員檢測(cè)內(nèi)存問(wèn)題,比如:任務(wù)管理器,System Monitor AcitvieX Control, 以及Rational的Purify。
而GC可以完全不需要開(kāi)發(fā)人員去關(guān)注什么時(shí)候釋放內(nèi)存。然而,垃圾回收器并不是可以管理內(nèi)存中的所有資源。有些資源垃圾回收器不知道該如何回收他們,這部分資源就需要開(kāi)發(fā)人員自己寫代碼實(shí)現(xiàn)回收。在.Net framework中,開(kāi)發(fā)人員通常會(huì)把清理這類資源的代碼寫到Close、Dispose或者Finalize方法中,稍后我們會(huì)看下Finalize方法,這個(gè)方法垃圾回收器會(huì)自動(dòng)調(diào)用。
不過(guò),有很多對(duì)象是不需要自己實(shí)現(xiàn)釋放資源的代碼的,比如:Rectangle,清空它只需要清空它的left,right,width,height字段就可以了,這垃圾回收器完全可以做。下面讓我們來(lái)看下內(nèi)存是如何分配給對(duì)象使用的。
對(duì)象分配:
.Net clr把所有的引用對(duì)象都分配到托管堆上。這一點(diǎn)很像c-runtime堆,不過(guò)你不需要關(guān)注什么時(shí)候釋放對(duì)象,對(duì)象會(huì)在不用時(shí)自動(dòng)釋放。這樣,就出現(xiàn)一個(gè)問(wèn)題,垃圾回收器是怎么知道一個(gè)對(duì)象不再使用該回收了呢?我們稍后解釋這個(gè)問(wèn)題。
現(xiàn)在有幾種垃圾回收算法,每一種算法都為一種特定的環(huán)境做了性能優(yōu)化,這篇文章我們關(guān)注的是clr的垃圾回收算法。讓我們從一個(gè)基礎(chǔ)概念談起。
當(dāng)一個(gè)進(jìn)程初始化之后,運(yùn)行時(shí)會(huì)保留一段連續(xù)的空白內(nèi)存空間,這塊內(nèi)存空間就是托管堆。托管堆會(huì)記錄一個(gè)指針,我們叫它NextObjPtr,這個(gè)指針指向下一個(gè)對(duì)象的分配地址,最初的時(shí)候,這個(gè)指針指向托管堆的起始位置。
應(yīng)用程序使用new操作符創(chuàng)建一個(gè)新對(duì)象,這個(gè)操作符首先要確認(rèn)托管堆剩余空間能放得下這個(gè)對(duì)象,如果能放得下,就把NextObjPtr指針指向這個(gè)對(duì)象,然后調(diào)用對(duì)象的構(gòu)造函數(shù),new操作符返回對(duì)象的地址。
圖1托管堆
這時(shí)候,NextObjPtr指向托管堆上下一個(gè)對(duì)象分配的位置,圖1顯示一個(gè)托管堆中有三個(gè)對(duì)象A、B和C。下一個(gè)對(duì)象會(huì)放在NextObjPtr指向的位置(緊挨著C對(duì)象)
現(xiàn)在讓我們?cè)倏匆幌耤-runtime堆如何分配內(nèi)存。在c-runtime堆,分配內(nèi)存需要遍歷一個(gè)鏈表的數(shù)據(jù)結(jié)構(gòu),直到找到一個(gè)足夠大的內(nèi)存塊,這個(gè)內(nèi)存塊有可能會(huì)被拆分,拆分后鏈表中的指針要指向剩余內(nèi)存空間,要確保鏈表的完好。對(duì)于托管堆,分配一個(gè)對(duì)象只是修改NextObjPtr指針的指向,這個(gè)速度是非常快的。事實(shí)上,在托管堆上分配一個(gè)對(duì)象和在線程棧上分配內(nèi)存的速度很接近。
到目前為止,托管堆上分配內(nèi)存的速度似乎比在c-runtime堆上的更快,實(shí)現(xiàn)上也更簡(jiǎn)單一些。當(dāng)然,托管堆獲得這個(gè)優(yōu)勢(shì)是因?yàn)樽隽艘粋€(gè)假設(shè):地址空間是無(wú)限的。很顯然這個(gè)假設(shè)是錯(cuò)誤的。必須有一種機(jī)制保證這個(gè)假設(shè)成立。這個(gè)機(jī)制就是垃圾回收器。讓我們看下它如何工作。
當(dāng)應(yīng)用程序調(diào)用new操作符創(chuàng)建對(duì)象時(shí),有可能已經(jīng)沒(méi)有內(nèi)存來(lái)存放這個(gè)對(duì)象了。托管堆可以檢測(cè)到NextObjPtr指向的空間是否超過(guò)了堆的大小,如果超過(guò)了就說(shuō)明托管堆滿了,就需要做一次垃圾回收了。
在現(xiàn)實(shí)中,在0代堆滿了之后就會(huì)觸發(fā)一次垃圾回收。“代”是垃圾回收器提升性能的一種實(shí)現(xiàn)機(jī)制。“代”的意思是:新創(chuàng)建的對(duì)象是年輕一代,而在回收操作發(fā)生之前沒(méi)有被回收掉的對(duì)象是較老的對(duì)象。將對(duì)象分成幾代可以允許垃圾回收器只回收某一代的對(duì)象,而不是回收所有對(duì)象。
垃圾回收算法:
垃圾回收器檢查看是否存在應(yīng)用程序不再使用的對(duì)象。如果這樣的對(duì)象存在,那么這些對(duì)象占用的空間就可以被回收(如果堆上沒(méi)有足夠的內(nèi)存可用,那么new操作符就會(huì)拋出OutofMemoryException)。你可能會(huì)問(wèn)垃圾回收器是怎樣判斷一個(gè)對(duì)象是否還在用呢?這個(gè)問(wèn)題不太容易得到答案。
每個(gè)應(yīng)用程序都有一組根對(duì)象,根是一些存儲(chǔ)位置,他們可能指向托管堆上的某個(gè)地址,也可能是null。例如,所有的全局和靜態(tài)對(duì)象指針是應(yīng)用程序的根對(duì)象,另外在線程棧上的局部變量/參數(shù)也是應(yīng)用程序的根對(duì)象,還有CPU寄存器中的指向托管堆的對(duì)象也是根對(duì)象。存活的根對(duì)象列表由JIT(just-in-time)編譯器和clr維護(hù),垃圾回收器可以訪問(wèn)這些根對(duì)象的。
當(dāng)垃圾回收器開(kāi)始運(yùn)行,它會(huì)假設(shè)托管堆上的所有對(duì)象都是垃圾。也就是說(shuō),假定沒(méi)有根對(duì)象,也沒(méi)有根對(duì)象引用的對(duì)象。然后垃圾回收器開(kāi)始遍歷根對(duì)象并構(gòu)建一個(gè)由所有和根對(duì)象之間有引用關(guān)系對(duì)象構(gòu)成的圖。
圖2顯示,托管堆上應(yīng)用程序的根對(duì)象是A,C,D和F,這幾個(gè)對(duì)象就是圖的一部分,然后對(duì)象D引用了對(duì)象H,那么對(duì)象H也被添加到圖中;垃圾回收器會(huì)循環(huán)遍歷所有可達(dá)對(duì)象。
圖2 托管堆上的對(duì)象
垃圾回收器會(huì)挨個(gè)遍歷根對(duì)象和引用對(duì)象。如果垃圾回收器發(fā)現(xiàn)一個(gè)對(duì)象已經(jīng)在圖中就會(huì)換一個(gè)路徑繼續(xù)遍歷。這樣做有兩個(gè)目的:一是提高性能,二是避免無(wú)限循環(huán)。
所有的根對(duì)象都檢查完之后,垃圾回收器的圖中就有了應(yīng)用程序中所有的可達(dá)對(duì)象。托管堆上所有不在這個(gè)圖上的對(duì)象就是要做回收的垃圾對(duì)象了。構(gòu)建好可達(dá)對(duì)象圖之后垃圾回收器開(kāi)始線性的遍歷托管堆,找到連續(xù)垃圾對(duì)象塊(可以認(rèn)為是空閑內(nèi)存)。然后垃圾回收器將非垃圾對(duì)象移動(dòng)到一起(使用c語(yǔ)言中的memcpy函數(shù)),覆蓋所有的內(nèi)存碎片。當(dāng)然,移動(dòng)對(duì)象時(shí)要禁用所有對(duì)象的指針(因?yàn)樗麄兌伎赡苁清e(cuò)誤的了)。因此垃圾回收器必須修改應(yīng)用程序的根對(duì)象使他們指向?qū)ο蟮男聝?nèi)存地址。此外,如果某個(gè)對(duì)象包含另一個(gè)對(duì)象的指針,垃圾回收器也要負(fù)責(zé)修改引用。圖3顯示了一次回收之后的托管堆。
圖3 回收之后的托管堆
如圖3所示在回收之后,所有的垃圾對(duì)象都被標(biāo)識(shí)出來(lái),而所有的非垃圾對(duì)象被移動(dòng)到一起。所有的非垃圾對(duì)象的指針也被修改成移動(dòng)后的內(nèi)存地址,NextObjPtr指向最后一個(gè)非垃圾對(duì)象的后面。這時(shí)候new操作符就可以繼續(xù)成功的創(chuàng)建對(duì)象了。
如你看到的,垃圾回收會(huì)有顯著的性能損失,這是使用托管堆的一個(gè)明顯的缺點(diǎn)。 不過(guò),要記著內(nèi)存回收操作旨在托管堆慢了之后才會(huì)執(zhí)行。在滿之前托管堆的性能比c-runtime堆的性能好要好。運(yùn)行時(shí)垃圾回收器還會(huì)做一些性能優(yōu)化,我們?cè)谙乱黄恼轮姓務(wù)撨@個(gè)。
下面的代碼說(shuō)明了對(duì)象是如何被創(chuàng)建管理的:
也許你會(huì)問(wèn),GC這么好,為什么ANSI C++中沒(méi)有它呢? 原因是垃圾回收器必須能找到應(yīng)用程序的根對(duì)象列表,必須找到對(duì)象的指針。而在C++中對(duì)象的指針之間是可以相互轉(zhuǎn)換的,沒(méi)有辦法知道指針指向的是一個(gè)什么對(duì)象的指針。在CLR中,托管堆知道對(duì)象的實(shí)際類型。而元數(shù)據(jù)(metadata)信息可以用來(lái)判斷對(duì)象引用了什么成員對(duì)象。
垃圾回收和Finalization
垃圾回收器提供了一個(gè)額外的功能,它可以在對(duì)象被標(biāo)識(shí)為垃圾后自動(dòng)調(diào)用其Finalize方法(前提是對(duì)象重寫了object的Finalize方法)。
Finalize方法是object對(duì)象的一個(gè)虛方法,如果需要你可以重寫這個(gè)方法,但是這個(gè)方法只能通過(guò)類似c++析構(gòu)函數(shù)的方式重寫。例如:
這里用過(guò)C++的程序員要特別注意,Finalize方法的寫法和C++的析構(gòu)函數(shù)完全一樣,但是,.Net 中的Finalize方法和析構(gòu)函數(shù)的卻是不一樣的,托管對(duì)象是不能被析構(gòu)的,只能通過(guò)垃圾回收回收。
當(dāng)你設(shè)計(jì)一個(gè)類時(shí),最好避免重寫Finalize方法,原因如下:
1. 實(shí)現(xiàn)Finalize的對(duì)象會(huì)被提升到更老的“代”,這會(huì)增加內(nèi)存壓力,使對(duì)象和此對(duì)象的關(guān)聯(lián)對(duì)象不能在成為垃圾的第一時(shí)間回收掉。
2. 這些對(duì)象分配時(shí)間會(huì)更長(zhǎng)
3. 讓垃圾回收器執(zhí)行Finalize方法會(huì)明顯的損耗性能。請(qǐng)記住,每一個(gè)實(shí)現(xiàn)了Finalize方法的對(duì)象都需要執(zhí)行Finalize方法,如果有一個(gè)長(zhǎng)度為10000的數(shù)組對(duì)象,每個(gè)對(duì)象都需要執(zhí)行Finalize方法
4. 重寫Finalize方法的對(duì)象可能會(huì) 引用其他沒(méi)有實(shí)現(xiàn)Finalize方法的對(duì)象,這些對(duì)象也會(huì)延遲回收
5. 你沒(méi)有辦法控制什么時(shí)候執(zhí)行Finalize方法。如果要在Finalize方法中釋放類似數(shù)據(jù)庫(kù)連接之類的資源,就有可能導(dǎo)致數(shù)據(jù)庫(kù)資源在時(shí)候后很久才得以釋放
6. 當(dāng)程序崩潰時(shí),一些對(duì)象還被引用,他們的Finalize方法就沒(méi)有機(jī)會(huì)執(zhí)行了。這種情況會(huì)在后臺(tái)線程使用對(duì)象,或者對(duì)象在程序退出時(shí),或者AppDomain卸載時(shí)。另外,默認(rèn)情況下,當(dāng)應(yīng)用程序被強(qiáng)制結(jié)束時(shí)Finalize方法也不會(huì)執(zhí)行。當(dāng)然所有的操作系統(tǒng)資源會(huì)被回收;但是在托管堆上的對(duì)象不會(huì)回收。你可以通過(guò)調(diào)用GC的RequestFinalizeOnShutdown方法改變這種行為。
7. 運(yùn)行時(shí)不能控制多個(gè)對(duì)象Finalize方法執(zhí)行的順序。而有時(shí)候?qū)ο蟮匿N毀可能有順序性
如果你定義的對(duì)象必須實(shí)現(xiàn)Finalize方法,那么要確保Finalize方法盡可能快的執(zhí)行,要避免所有可能引起阻塞的操作,包括任何線程同步操作。另外,要確保Finalize方法不會(huì)引起任何異常,如果有異常垃圾回收器會(huì)繼續(xù)執(zhí)行其他對(duì)象的Finalize方法直接忽略掉異常。
當(dāng)編譯器生成代碼時(shí)會(huì)自動(dòng)在構(gòu)造函數(shù)上調(diào)用基類的構(gòu)造函數(shù)。同樣C++的編譯器也會(huì)為析構(gòu)函數(shù)自動(dòng)添加基類析構(gòu)函數(shù)的調(diào)用。但是,.Net中的Finalize函數(shù)不是這樣子,編譯器不會(huì)對(duì)Finalize方法做特殊處理。如果你想在Finalize方法中調(diào)用父類的Finalize方法,必須自己顯示添加調(diào)用代碼。
請(qǐng)注意在C#中Finalize方法的寫法和c++中的析構(gòu)函數(shù)一樣,但是C#不支持析構(gòu)函數(shù),不要讓這種寫法欺騙你。
GC調(diào)用Finalize方法的內(nèi)部實(shí)現(xiàn)
表面看,垃圾回收器嗲用Finalize方法很簡(jiǎn)單,你創(chuàng)建一個(gè)對(duì)象,當(dāng)對(duì)象回收時(shí)調(diào)用它的Finalize方法。但是事實(shí)上要復(fù)雜一些。
當(dāng)應(yīng)用程序創(chuàng)建一個(gè)新對(duì)象時(shí),new操作符在堆上分配內(nèi)存。如果對(duì)象實(shí)現(xiàn)了Finalize方法。對(duì)象的指針會(huì)放到終結(jié)隊(duì)列中。終結(jié)隊(duì)列是由垃圾回收器控制的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。在隊(duì)列中每一個(gè)對(duì)象在回收時(shí)都需要調(diào)用它們的Finalize方法。
下圖顯示的堆上包含幾個(gè)對(duì)象,其中一些對(duì)象是跟對(duì)象,一些對(duì)象不是。當(dāng)對(duì)象C、E、F、I和J創(chuàng)建時(shí),系統(tǒng)會(huì)檢測(cè)這些對(duì)象實(shí)現(xiàn)了Finalize方法,并將它們的指針?lè)诺浇K結(jié)隊(duì)列中。
Finalize方法要做的事情通常是回收垃圾回收器不能回收的資源,例如文件句柄,數(shù)據(jù)庫(kù)連接等等。
當(dāng)垃圾回收時(shí),對(duì)象B、E、G、H、I和J被標(biāo)記為垃圾。垃圾回收器掃描終結(jié)隊(duì)列找到這些對(duì)象的指針。當(dāng)發(fā)現(xiàn)對(duì)象指針時(shí),指針會(huì)被移動(dòng)到Freachable隊(duì)列。Freachable隊(duì)列是另一個(gè)由垃圾回收器控制的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。在Freachable隊(duì)列中的每一個(gè)對(duì)象的Finalize方法將執(zhí)行。
垃圾回收之后,托管堆如圖6所示。你可以看到對(duì)象B、G、H已經(jīng)被回收了,因?yàn)檫@幾個(gè)對(duì)象沒(méi)有Finalize方法。然而對(duì)象E、I、J還沒(méi)有被回收掉,因?yàn)樗麄兊腇inalize方法還沒(méi)有執(zhí)行。
圖5 垃圾回收后的托管堆
程序運(yùn)行時(shí)會(huì)有一個(gè)專門的線程負(fù)責(zé)調(diào)用Freachable隊(duì)列中對(duì)象的Finalize方法。當(dāng)Freachable隊(duì)列為空時(shí),這個(gè)線程會(huì)休眠,當(dāng)隊(duì)列中有對(duì)象時(shí),線程被喚醒,移除隊(duì)列中的對(duì)象,并調(diào)用它們的Finalize方法。因此在執(zhí)行Finalize方法時(shí)不要企圖訪問(wèn)線程的local storage。
終結(jié)隊(duì)列(finalization queue)和Freachable隊(duì)列之間的交互很巧妙。首先讓我告訴你freachable的名字是怎么來(lái)的。F顯然是finalization;在此隊(duì)列中的每一個(gè)對(duì)象都在等待執(zhí)行他們的Finalize方法;reachable意思是這些對(duì)象來(lái)了。另一種說(shuō)法,Freachable隊(duì)列中的對(duì)象被認(rèn)為是跟對(duì)象,就像是全局變量或靜態(tài)變量。因此,如果一個(gè)對(duì)象在freachable隊(duì)列中,那么這個(gè)對(duì)象就不是垃圾。
簡(jiǎn)短點(diǎn)說(shuō),當(dāng)一個(gè)對(duì)象是不可達(dá)的,垃圾回收器會(huì)認(rèn)為這個(gè)對(duì)象是垃圾。那么,當(dāng)垃圾回收器將對(duì)象從終結(jié)隊(duì)列移動(dòng)到Freachable隊(duì)列中,這些對(duì)象就不再是垃圾了,它們的內(nèi)存也不會(huì)回收。從這一點(diǎn)上來(lái)講,垃圾回收器已經(jīng)完成標(biāo)識(shí)垃圾,一些對(duì)象被標(biāo)識(shí)成垃圾又被重新認(rèn)為成非垃圾對(duì)象。垃圾回收器回收壓縮內(nèi)存,清空f(shuō)reachable隊(duì)列,執(zhí)行隊(duì)列中每一個(gè)對(duì)象的Finalize方法。
圖6 再次執(zhí)行垃圾回收后的托管堆
再次出發(fā)垃圾回收之后,實(shí)現(xiàn)Finalize方法的對(duì)象才被真正的回收。這些對(duì)象的Finalize方法已經(jīng)執(zhí)行過(guò)了,Freachable隊(duì)列清空了。
垃圾回收讓對(duì)象復(fù)活
在前面部分我們已經(jīng)說(shuō)了,當(dāng)程序不使用某個(gè)對(duì)象時(shí),這個(gè)對(duì)象會(huì)被回收。然而,如果對(duì)象實(shí)現(xiàn)了Finalize方法,只有當(dāng)對(duì)象的Finalize方法執(zhí)行之后才會(huì)認(rèn)為這個(gè)對(duì)象是可回收對(duì)象并真正回收其內(nèi)存。換句話說(shuō),這類對(duì)象會(huì)先被標(biāo)識(shí)為垃圾,然后放到freachable隊(duì)列中復(fù)活,然后執(zhí)行Finalize之后才被回收。正是Finalize方法的調(diào)用,讓這種對(duì)象有機(jī)會(huì)復(fù)活,我們可以在Finalize方法中讓某個(gè)對(duì)象強(qiáng)引用這個(gè)對(duì)象;那么垃圾回收器就認(rèn)為這個(gè)對(duì)象不再是垃圾了,對(duì)象就復(fù)活了。
如下復(fù)活演示代碼:
在這種情況下,當(dāng)對(duì)象的Finalize方法執(zhí)行之后,對(duì)象被Application的靜態(tài)字段ObjHolder強(qiáng)引用,成為根對(duì)象。這個(gè)對(duì)象就復(fù)活了,而這個(gè)對(duì)象引用的對(duì)象也就復(fù)活了,但是這些對(duì)象的Finalize方法可能已經(jīng)執(zhí)行過(guò)了,可能會(huì)有意想不到的錯(cuò)誤發(fā)生。
事實(shí)上,當(dāng)你設(shè)計(jì)自己的類型時(shí),對(duì)象的終結(jié)和復(fù)活有可能完全不可控制。這不是一個(gè)好現(xiàn)象;處理這種情況的常用做法是在類中定義一個(gè)bool變量來(lái)表示對(duì)象是否執(zhí)行過(guò)了Finalize方法,如果執(zhí)行過(guò)Finalize方法,再執(zhí)行其他方法時(shí)就拋出異常。
現(xiàn)在,如果有其他的代碼片段又將Application.ObjHolder設(shè)置為null,這個(gè)對(duì)象變成不可達(dá)對(duì)象。最終垃圾回收器會(huì)把對(duì)象當(dāng)成垃圾并回收對(duì)象內(nèi)存。請(qǐng)注意這一次對(duì)象不會(huì)出現(xiàn)在finalization隊(duì)列中,它的Finalize方法也不會(huì)再執(zhí)行了。
復(fù)活只有有限的幾種用處,你應(yīng)該盡可能避免使用復(fù)活。盡管如此,當(dāng)使用復(fù)活時(shí),最好重新將對(duì)象添加到終結(jié)隊(duì)列中,GC提供了靜態(tài)方法ReRegisterForFinalize方法做這件事:
如下代碼:
public class Foo{ ~Foo(){ Application.ObjHolder = this; GC.ReRegisterForFinalize(this); } } 當(dāng)對(duì)象復(fù)活時(shí),重新將對(duì)象添加到復(fù)活隊(duì)列中。需要注意的時(shí)如果一個(gè)對(duì)象已經(jīng)在終結(jié)隊(duì)列中,然后又調(diào)用了GC.ReRegisterForFinalize(obj)方法會(huì)導(dǎo)致此對(duì)象的Finalize方法重復(fù)執(zhí)行。垃圾回收機(jī)制的目的是為開(kāi)發(fā)人員簡(jiǎn)化內(nèi)存管理。
下一篇我們談一下弱引用的作用,垃圾回收中的“代”,多線程中的垃圾回收和與垃圾回收相關(guān)的性能計(jì)數(shù)器。
總結(jié)
以上是生活随笔為你收集整理的.Net 垃圾回收机制原理(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一个小时学会MySQL数据库
- 下一篇: bernoulli_Python-Ber