解讀.net垃圾回收和CLR 4.0對(duì)垃圾回收所做的改進(jìn)之二_.Net教程
推薦:如何改變.net網(wǎng)站的默認(rèn)解決方案位置1. 把該網(wǎng)站以前的解凍方案刪除(默認(rèn)位置:我的文檔\Visual Studio 2005\Projects\XX\xx.sln) 2. 打開(kāi)VS2005,選擇
A survey of garbage collection and the changes CLR 4.0 brings in Part 2 - series of what is new in CLR 4.0
接前篇Continue the previous post .net垃圾回收和CLR 4.0對(duì)垃圾回收所做的改進(jìn)之一
CLR4.0所帶來(lái)的變化仍然沒(méi)有在這篇,請(qǐng)看下篇。
內(nèi)存釋放和壓縮
創(chuàng)建對(duì)象引用圖之后,垃圾回收器將那些沒(méi)有在這個(gè)圖中的對(duì)象(即不再需要的對(duì)象)釋放。釋放內(nèi)存之后, 出現(xiàn)了內(nèi)存碎片, 垃圾回收器掃描托管堆,找到連續(xù)的內(nèi)存塊,然后移動(dòng)未回收的對(duì)象到更低的地址, 以得到整塊的內(nèi)存,同時(shí)所有的對(duì)象引用都將被調(diào)整為指向?qū)ο笮碌拇鎯?chǔ)位置。這就象一個(gè)夯實(shí)的動(dòng)作。
After building up the reference relationship graph, garbage collector reclaims the objects not in the graph(no longer needed), after releasing the objects not in the graph, there is memory scrap. Garbage collector scans the managed heap to find continous memory block, and shifts the remaining objects to lower address to get consecutive memory space, and then adjusts the references of objects according to the shifted address of objects. This is looking like a tamping on the managed heap.
下面要說(shuō)到的是代的概念。代概念的引入是為了提高垃圾收集器的整體性能。We come to the concept of generations next. The importing of generation concept is to improve the performance of garbage collector.
代Generations
請(qǐng)想一想如果垃圾收集器每次總是掃描所有托管堆中的對(duì)象,對(duì)性能會(huì)有什么影響。會(huì)不會(huì)很慢?是的。微軟因此引入了代的概念。
Please think about what will happen if garbage collector scans all the objects in the whole heap in every garbage collecting cycle. Will it be very slow? Yes, therefore Microsoft imported the concept of generations.
為什么代的概念可以提高垃圾收集器的性能?因?yàn)槲④浭腔趯?duì)大量編程實(shí)踐的科學(xué)估計(jì),做了一些假定而這些假定符合絕大多數(shù)的編程實(shí)踐:
Why generation concept can help improve performance of garbage collector? Because Microsoft did scientific valuation on mass of programming practice, and made assumptions and the assumptions conform to most of programming practice:
越新的對(duì)象,其生命周期越短。The newer an object is, the shorter its lifetime will be.
越老的對(duì)象,其生命周越長(zhǎng)。The older an object is, the longer its lifetime will be.
新對(duì)象之間通常有強(qiáng)的關(guān)系并被同時(shí)訪問(wèn)。Newer objects tend to have strong relationships to each other and are frequently accessed around the same time.
壓縮一部分堆比壓縮整個(gè)堆快。Compacting a portion of the heap is faster than compacting the whole heap.
有了代的概念,垃圾回收活動(dòng)就可以大部分局限于一個(gè)較小的區(qū)域來(lái)進(jìn)行。這樣就對(duì)垃圾回收的性能有所提高。After importing the concept of generations, most of garbage collecting will be limited in in smaller range of memory. This enhances the performance of garbage collector.
讓我們來(lái)看垃圾收集器具體是怎么實(shí)現(xiàn)代的: Let’s see how generations are exactly implemented in garbage collector:
第0代:新建對(duì)象和從未經(jīng)過(guò)垃圾回收對(duì)象的集合 Generation 0: A collection of newly created object and the objects never collected.
第1代:在第0代收集活動(dòng)中未回收的對(duì)象集合 Generation 1: A collection of objects not collected by garbage collector in collecting cycle of generation 0.
第2代:在第1和第2代中未回收的對(duì)象集合, 即垃圾收集器最高只支持到第2代, 如果某個(gè)對(duì)象在第2代的回收活動(dòng)中留下來(lái),它仍呆在第2代的內(nèi)存中。 Generation 2: A collection of objects not collected by garbage collector in generation 1 and generation 2. This means the highest generation that garbage collector supports is generation 2. If an object survives in generation 2 collecting cycle, it still remains in memory of generation 2.
當(dāng)程序剛開(kāi)始運(yùn)行,垃圾收集器分配為每一代分配了一定的內(nèi)存,這些內(nèi)存的初始大小由.net framework的策略決定。垃圾收集器記錄了這三代的內(nèi)存起始地址和大小。這三代的內(nèi)存是連接在一起的。第2代的內(nèi)存在第1代內(nèi)存之下,第1代內(nèi)存在第0代內(nèi)存之下。應(yīng)用程序分配新的托管對(duì)象總是從第0代中分配。如果第0代中內(nèi)存足夠,CLR就很簡(jiǎn)單快速地移動(dòng)一下指針,完成內(nèi)存的分配。這是很快速的。當(dāng)?shù)?代內(nèi)存不足以容納新的對(duì)象時(shí),就觸發(fā)垃圾收集器工作,來(lái)回收第0代中不再需要的對(duì)象,當(dāng)回收完畢,垃圾收集器就夯實(shí)第0代中沒(méi)有回收的對(duì)象至低的地址,同時(shí)移動(dòng)指針至空閑空間的開(kāi)始地址(同時(shí)按照移動(dòng)后的地址去更新那些相關(guān)引用),此時(shí)第0代就空了,因?yàn)槟切┰诘?代中沒(méi)有回收的對(duì)象都移到了第1代。
When the program initializes, garbage collector allocates memory for generations. The initial size of memory blocks are determined according to the strategies of the .net framework. Garbage collector records the start address and size of the memory block for generations. The memory blocks of generations are continuous and adjacent. The memory of generation 2 is under the memory of generation 1, and the memory of generation 1 is under the memory of generation 0. CLR always allocates memory for new objects in generation 0. If there is enough memory in generation 0, CLR simply moves the pointer to allocate memory. This is really fast. When there is not enough memory in generation 0 to accommodate new objects, CLR triggers garbage collector starts to collect objects no longer needed from generation 0. When the collecting action in generation 0 finishs, garbage collector tamps(or compacts) the objects not collected in generation 0 to lower address, and moves the pointer to start address of free memory(and updates the related references according to the shifted address of objects). At this time, generation 0 is empty, because the objects survived in generation 0 are moved to generation 1.
當(dāng)只對(duì)第0代進(jìn)行收集時(shí),所發(fā)生的就是部分收集。這與之前所說(shuō)的全部收集有所區(qū)別(因?yàn)榇囊?。對(duì)第0代收集時(shí),同樣是從根開(kāi)始找那些正引用的對(duì)象,但接下來(lái)的步驟有所不同。當(dāng)垃圾收集器找到一個(gè)指向第1代或者第2代地址的根,垃圾收集器就忽略此根,繼續(xù)找其他根,如果找到一個(gè)指向第0代對(duì)象的根,就將此對(duì)象加入圖。這樣就可以只處理第0代內(nèi)存中的垃圾。這樣做有個(gè)先決條件,就是應(yīng)用程序此前沒(méi)有去寫第1代和第2代的內(nèi)存,沒(méi)有讓第1代或者第2代中某個(gè)對(duì)象指向第0代的內(nèi)存。但是實(shí)際中應(yīng)用程序是有可能寫第1代或者第2代的內(nèi)存的。針對(duì)這種情況,CLR有專門的數(shù)據(jù)結(jié)構(gòu)(Card table)來(lái)標(biāo)志應(yīng)用程序是否曾經(jīng)寫第1代或者第2代的內(nèi)存。如果在此次對(duì)第0代進(jìn)行收集之前,應(yīng)用程序?qū)戇^(guò)第1代或者第2代的內(nèi)存,那些被Card Table登記的對(duì)象(在第1代或者第2代)將也要在此次對(duì)第0代收集時(shí)作為根。這樣,才可以正確地對(duì)第0代進(jìn)行收集。
When collecting generation 0 only, it is partial collection. It is different from full collection mentioend earlier(because of the generations). When collecting generation 0, garbage collector starts from the roots, which is the same as the full collection, but it is different in coming steps. When garbage collector finds a root pointing to an address of generation 1 or 2, garbage collector ignores the root, and goes to next root. If garbage collector finds a root pointing to an object of generation 0, garbage collector addes the object into the graph. That way garbage collector processes the objects of generation 0 only. There is a pre-condition to do that. It is that the application does not write to the memory of generation 1 and 2, does not allow some objects of generation 1 or 2 refer to the memory of generation 0. But in our daily work, the applicaiton is possible to write the memory of generation 1 or 2. In this case, CLR has a dedicated data structure called Card Table to record whether the application writes the memory of generation 1 or 2. If the application writes the memory of generation 1 or 2 before the collecting on generation 0, the objects recorded by the Card Table will become roots during the collecting on generation 0. Garbage collection on generation 0 can be done correctly in this case.
以上說(shuō)到了第0代收集發(fā)生的一個(gè)條件,即第0代沒(méi)有足夠內(nèi)存去容納新對(duì)象。執(zhí)行GC.Collect()也會(huì)觸發(fā)對(duì)第0代的收集。另外,垃圾收集器還為每一代都維護(hù)著一個(gè)監(jiān)視閥值。第0代內(nèi)存達(dá)到這個(gè)第0代的閥值時(shí)也會(huì)觸發(fā)對(duì)第0代的收集。對(duì)第1代的收集發(fā)生在執(zhí)行GC.Collect(1)或者第1代內(nèi)存達(dá)到第1代的閥值時(shí)。第2代也有類似的觸發(fā)條件。當(dāng)?shù)?代收集時(shí),第0代也需要收集。當(dāng)?shù)?代收集時(shí),第1和第0代也需要收集。在第n代收集之后仍然存留下來(lái)的對(duì)象將被轉(zhuǎn)移到第n+1代的內(nèi)存中,如果n=2, 那么存留下來(lái)的對(duì)象還將留在第2代中。
We mentioned a criteria to trigger collecting on generation 0 in above paragraphs: generation 0 does not have enough memory to accommodate new objects. When execute GC.Collect(), it launches collecting on generation 0 also. In addition, garbage collector sets up a threshold for each of generations. When the memory of generation 0 reaches the threshold, collecting on generation 0 happens also. Collecting on generation 1 happens when executing GC.Collect() or the memory of generation 1 reaches the threshold of generation 1. Generation 2 has similar trigger conditions. When collecting on generation 1, collecting on generation 0 happens also. When collecting on generation 2, collecting on generation 1 and 0 happen also. The survived object in collecting generation n will be moved to the memory of generation n+1. If n=2, the remaining objects still stay in generation 2.
對(duì)象結(jié)束Finalization of objects
對(duì)象結(jié)束機(jī)制是程序員忘記用Close或者Dispose等方法清理申請(qǐng)的資源時(shí)的一個(gè)保證措施。如下的一個(gè)類,當(dāng)一個(gè)此類的實(shí)例創(chuàng)建時(shí),在第0代中分配內(nèi)存,同時(shí)此對(duì)象的引用要被加入到一個(gè)由CLR維護(hù)的結(jié)束隊(duì)列中去。
Finalization is an ensuring mechanism when programmers forget to use Close or Dispose method to clean up resources. For exmaple, a class like the following, when an instane of the class is created, it is allocated in memory of generation 0, and a reference of the object is appended to Finalization quere maintained by CLR.
public class BaseObj {
public BaseObj() { }
protected override void Finalize() {
// Perform resource cleanup code here...
// Example: Close file/Close network connection
Console.WriteLine("In Finalize.");
}
}
當(dāng)此對(duì)象成為垃圾時(shí),垃圾收集器將其引用從結(jié)束隊(duì)列移到待結(jié)束隊(duì)列中,同時(shí)此對(duì)象會(huì)被加入引用關(guān)系圖。一個(gè)獨(dú)立運(yùn)行的CLR線程將一個(gè)個(gè)從待結(jié)束隊(duì)列(Jeffrey Richter稱之為Freachable quere)取出對(duì)象,執(zhí)行其Finalize方法以清理資源。因此,此對(duì)象不會(huì)馬上被垃圾收集器回收。只有當(dāng)此對(duì)象的Finalize方法被執(zhí)行完畢后,其引用才會(huì)從待結(jié)束隊(duì)列中移除。等下一輪回收時(shí),垃圾回收器才會(huì)將其回收。
When the object becomes garbage, garbage collector moves the reference from Finalization queue to ToBeFinalized queue(Jeffrey Richter called it Freachable queue), and appends the object to the reference graph. A standalone thread of CLR will fetch objects from the ToBeFinalized queue one by one, and execute the Finalize() method of objects to clean up resources. Therefore, the object will not be collected right away by garbage collector. After the Finalize() method is executed, its reference will be removed from the ToBeFinalizaed queue. When next collecting comes, garbage collector reclaims its memory.
GC類有兩個(gè)公共靜態(tài)方法GC.ReRegisterForFinalize和GC.SuppressFinalize大家也許想了解一下,ReRegisterForFinalize是將指向?qū)ο蟮囊锰砑拥浇Y(jié)束隊(duì)列中(即表明此對(duì)象需要結(jié)束),SuppressFinalize是將結(jié)束隊(duì)列中該對(duì)象的引用移除,CLR將不再會(huì)執(zhí)行其Finalize方法。
There are two public static methods of GC class you guys may want to know: GC.ReRegisterForFinalize and GC.SuppressFinalize. ReRegisterForFinalize is to append the reference of objects to finalization queue(meaning the objects need to be finalized), SuppressFinalize is to remove the reference of objects from finalization queue, then CLR would not execute the Finalize method of the object.
因?yàn)橛蠪inalize方法的對(duì)象在new時(shí)就自動(dòng)會(huì)加入結(jié)束隊(duì)列中,所以ReRegisterForFinalize可以用的場(chǎng)合比較少。ReRegisterForFinalize比較典型的是配合重生(Resurrection)的場(chǎng)合來(lái)用。重生指的是在Finalize方法中讓根又重新指向此對(duì)象。那么此對(duì)象又成了可到達(dá)的對(duì)象,不會(huì)被垃圾收集器收集,但是此對(duì)象的引用未被加入結(jié)束隊(duì)列中。所以此處需要用ReRegisterForFinalize方法來(lái)將對(duì)象的引用添加到結(jié)束隊(duì)列中。因?yàn)橹厣旧碓诂F(xiàn)實(shí)應(yīng)用中就很少見(jiàn),所以ReRegisterForFinalize也將比較少用到。
Because the objects with Finalize method will be appended to Finalization queue when new operation, there are few scenarios to use ReRegisterForFinalize method. A typical scenario is to use ReRegisterForFinalize with Resurrection. Resurrection is that we let a root pointing to the object again in Finalize method, and then the object becomes reachable again, therefore it will be not collected by garbage collector. But the reference of the object has not been appended to Finalization queue, therefore we can use ReRegisterForFinalize to append the object to Finalization queue. Because there are few requirement in reality to use resurrection, ReRegisterForFinalize will have be used in low rate.
相比之下,SuppressFinalize更常用些。SuppressFinalize用于同時(shí)實(shí)現(xiàn)了Finalize方法和Dispose()方法來(lái)釋放資源的情況下。在Dispose()方法中調(diào)用GC.SuppressFinalize(this),那么CLR就不會(huì)執(zhí)行Finalize方法。Finalize方法是程序員忘記用Close或者Dispose等方法清理資源時(shí)的一個(gè)保證措施。如果程序員記得調(diào)用Dispose(),那么就會(huì)不執(zhí)行Finalize()來(lái)再次釋放資源;如果程序員忘記調(diào)用Dispose(), Finalize方法將是最后一個(gè)保證資源釋放的措施。這樣做不失為一種雙保險(xiǎn)的方案。
Compare to ReRegisterForFinalize, SuppressFinalize has more frequent utilization. When we implement both Finalize method and Dispose method to release resources, we need to use SuppressFinalize method. Call GC.SuppressFinalize(this) in Dispose() method body and then CLR will execute the Finalize method. Finalization is an ensuring mechanism when programmers forget to use Close or Dispose method to clean up resources. If programmers do call Dispose(), then CLR will not call Finalize method to release resources again. If programmers forget to call Dispose(), then Finalize method will be the final ensuring mechnism for resource releasing. That way it is dual fail-safe solution.
對(duì)象結(jié)束機(jī)制對(duì)垃圾收集器的性能影響比較大,同時(shí)CLR難以保證調(diào)用Finalize方法的時(shí)間和次序。因此,盡量不要用對(duì)象結(jié)束機(jī)制,而采用自定義的方法或者名為Close, Dispose的方法來(lái)清理資源�?梢钥紤]實(shí)現(xiàn)IDisposable接口并為Dispose方法寫好清理資源的方法體。
Finalization has significant impact on performance of garbage collector, and CLR can not be sure on the order to call Finalize methods of objects, therefore please do not use finalization of objects as possible as you can, instead, you could use self defined methods, Close method or Dispose method to clean up resources. Please think about to implement the IDisposable interface and write method body for the Dispose method.
大對(duì)象堆Large object heap
大對(duì)象堆專用于存放大于85000字節(jié)的對(duì)象。初始的大對(duì)象內(nèi)存區(qū)域堆通常在第0代內(nèi)存之上,并且與第0代內(nèi)存不鄰接。第0,第1和第2代合起來(lái)稱為小對(duì)象堆。CLR分配一個(gè)新的對(duì)象時(shí),如果其大小小于85000字節(jié),就在第0代中分配,如果其大小大于等于85000自己,就在大對(duì)象堆中分配。
Large object heap is to store objects that its size is over 85000 bytes. The initial memory block is above the memory block of generation 0, and it is not adjacent to memory block of generation 0. Generation 0,1 and 2 is called small object heap. When CLR allocates a new object, if its size is lower than 85000 bytes, then allocates memory in generation 0; If its size is over 85000 bytes, then allocates memory in large object heap.
因?yàn)榇髮?duì)象的尺寸比較大,收集時(shí)成本比較高,所以對(duì)大對(duì)象的收集是在第2代收集時(shí)。大對(duì)象的收集也是從根開(kāi)始查找可到達(dá)對(duì)象,那些不可到達(dá)的大對(duì)象就可回收。垃圾收集器回收了大對(duì)象后,不會(huì)對(duì)大對(duì)象堆進(jìn)行夯實(shí)操作(畢竟移動(dòng)大對(duì)象成本較高),而是用一個(gè)空閑對(duì)象表的數(shù)據(jù)結(jié)構(gòu)來(lái)登記哪些對(duì)象的空間可以再利用,其中兩個(gè)相鄰的大對(duì)象回收將在空閑對(duì)象表中作為一個(gè)對(duì)象對(duì)待�?臻e對(duì)象表登記的空間將可以再分配新的大對(duì)象。
Because size of large object is significant, the cost of collection is significant also. Collection of large objects happens when generation 2 collecting. Collection of large objects starts from the roots also and searches for reachable objects. Non-reachable large objects will be collected. After collecting non-reachable large objects, garbage collector will not tamp the large object heap(because the cost of moving a large object is high), instead, garbage collector uses a free object table to record memory ranges can be re-used, if there are two adjacent large object collected, then treats the two large objects as one large object in free object table. The memory ranges in free object table can be re-used by new large objects.
大對(duì)象的分配,回收的成本都較小對(duì)象高,因此在實(shí)踐中最好避免很快地分配大對(duì)象又很快回收,可以考慮如何分配一個(gè)大對(duì)象池,重復(fù)利用這個(gè)大對(duì)象池,而不頻繁地回收。
The cost of allocation and collection of large objects is higher than the cost of allocation and collection of small objects, therefore it would better avoid to allocate large object and release it soon. Please think about allocate a pool of large objects, try to re-use the pool of large objects, do not frequently reclaim large objects.
分享:解決ASP.NET中讓網(wǎng)頁(yè)彈出窗口的問(wèn)題由于項(xiàng)目要用到彈出窗口,或者叫做模擬窗口,這段時(shí)間就研究了下,解決方案很多,比如,window.open();window.showDialog();用層模擬,框架 DIV Javascript.... 用window.open();這種方法的缺點(diǎn)是,會(huì)被瀏覽器或者相關(guān)軟件給屏蔽掉。用層模擬的話,如果想實(shí)
- asp.net如何得到GRIDVIEW中某行某列值的方法
- .net SMTP發(fā)送Email實(shí)例(可帶附件)
- js實(shí)現(xiàn)廣告漂浮效果的小例子
- asp.net Repeater 數(shù)據(jù)綁定的具體實(shí)現(xiàn)
- Asp.Net 無(wú)刷新文件上傳并顯示進(jìn)度條的實(shí)現(xiàn)方法及思路
- Asp.net獲取客戶端IP常見(jiàn)代碼存在的偽造IP問(wèn)題探討
- VS2010 水晶報(bào)表的使用方法
- ASP.NET中操作SQL數(shù)據(jù)庫(kù)(連接字符串的配置及獲取)
- asp.net頁(yè)面?zhèn)髦禍y(cè)試實(shí)例代碼
- DataGridView - DataGridViewCheckBoxCell的使用介紹
- asp.net中javascript的引用(直接引入和間接引入)
- 三層+存儲(chǔ)過(guò)程實(shí)現(xiàn)分頁(yè)示例代碼
.Net教程Rss訂閱編程教程搜索
.Net教程推薦
- 脫離.Net Framework框架運(yùn)行.Net程序
- 無(wú)法將函數(shù)定義與現(xiàn)有的聲明匹配 問(wèn)題的解決辦法 分享
- 使用ASP.NET開(kāi)發(fā)移動(dòng)通訊的幾種方法
- Asp.net ajax實(shí)現(xiàn)任務(wù)提示頁(yè)面
- 基于C#的接口基礎(chǔ)教程之七
- 怎樣使.NET命名空間符合標(biāo)準(zhǔn)
- .NET中RDLC循環(huán)處理數(shù)據(jù)的應(yīng)用分析
- 解讀ASP.NET如何實(shí)現(xiàn)靜態(tài)的TreeView控件導(dǎo)航
- 模板無(wú)憂:asp.net后臺(tái)cs中的JSON格式變量在前臺(tái)Js中調(diào)用方法
- asp.net搜索關(guān)鍵詞高亮顯示函數(shù)
- 相關(guān)鏈接:
- 教程說(shuō)明:
.Net教程-解讀.net垃圾回收和CLR 4.0對(duì)垃圾回收所做的改進(jìn)之二
。