CLR自動管理內(nèi)存---《clr via c#》筆記
2012-02-03 16:27 海不是藍(lán) 閱讀(662) 評論(0) 收藏 舉報(bào)|
理解垃圾回收平臺的基本工作原理 |
面向?qū)ο蟮沫h(huán)境中,每個(gè)類型都代表可供程序使用的一種資源。要使用這些資源,就需要為資源分配內(nèi)存,當(dāng)不使用資源的時(shí)候再回收銷毀。過程如下。
1.調(diào)用IL指令newobj,為代表資源的類型分配內(nèi)存,c#中就是new操作符。
2.初始化內(nèi)存,設(shè)置資源為初始狀態(tài),c#中是類型的實(shí)例構(gòu)造器負(fù)責(zé)初始化。
3.訪問類型的成員(可以重復(fù))使用資源。(就是你平時(shí)不斷的使用一個(gè)對象的過程)
4.摧毀資源的狀態(tài)以進(jìn)行清理。
5.釋放內(nèi)存,垃圾回收器獨(dú)自負(fù)責(zé)這一步。
|
Jeffrey大牛的抱怨 |
Jeffrey這里有開始回憶他以前寫c++的光陰了!
Jeffrey抱怨:進(jìn)行非托管編程的時(shí)候,內(nèi)存管理不當(dāng)是主要的bug。
Jeffrey抱怨:花了大量的時(shí)間去考慮內(nèi)存的管理,極大的分散了他的精力。
但是CLR的垃圾回收來了!Jeffrey開心的笑了!

紅領(lǐng)巾???
|
從托管堆分配資源 |
CLR要求所有資源都從托管堆分配!別想逆天!
這個(gè)托管堆和C語言中的堆相似,不同的地方是托管堆不需要你去手動刪除里面的對象。程序不需要的對象它自己會刪除!
托管堆如何知道應(yīng)用程序不再用一個(gè)對象???
要想知道問題的答案?Jeffrey說:“親,稍后告訴你!”。

從基本概念講起:
CLR維護(hù)著一片連續(xù)的地址空間,最開始沒有對應(yīng)物理存儲空間,這個(gè)地址就是托管堆。托管堆還有個(gè)指針,叫“NextObjPtr”,它指向下一個(gè)對象在堆中的分配位置。
Newobj指令導(dǎo)致clr執(zhí)行的步驟
1.計(jì)算類型的字段需要的字節(jié)數(shù)。
2.加上對象的開銷需要的字節(jié)數(shù)。每個(gè)對象有2個(gè)開銷:一個(gè)是類型對象指針和一個(gè)同步索引塊。
3.CLR檢查保留區(qū)是否能提供分配對象所需的字節(jié)數(shù)。
這里有個(gè)彩蛋,托管堆中分配的對象內(nèi)存地址是連續(xù)的,例如c語言這些從線程棧中分配對象的內(nèi)存可能是分散的。
|
垃圾回收算法 |
垃圾回收器檢查托管堆中是否有程序不用的對象,如果有,那個(gè)對象的內(nèi)存將被回收。如果回收了一次,托管堆中還是沒有可以使用的內(nèi)存,那么new操作符將會拋出OutOfMemoryException異常!
然后Jeffrey開始解釋什么是根!
然后他寫了一個(gè)類,竟然搬上了32cpu下的80386匯編!
什么EBX,EDI這些東西,我早忘記了。直接不看了!
大概也就是從匯編角度去解釋clr怎么進(jìn)行回收的。
根和回收
每個(gè)程序都包含一組根,每個(gè)根都是一個(gè)存儲位置,其中包含了指向?qū)σ脤ο蟮囊粋€(gè)指針!該指針要么是托管堆中一個(gè)對象,要么是null。
1.類型中的靜態(tài)字段是一個(gè)根。
2.任何方法參數(shù)或局部變量也是一個(gè)根。
3.只有引用類型的變量才能被認(rèn)為是根,值類型的變量永遠(yuǎn)不會是根。
接下來就是那萬惡的匯編了!!!我懷著萬分痛苦的心情還是看了下。
1.標(biāo)記階段
大概意思就是垃圾回收器檢查托管堆中的每一個(gè)根,然后沿著這些根去檢查根下面是否有引用其他對象,如果有,先標(biāo)記好然后在根的同步索引字段上開啟一位。再檢查這個(gè)對象在堆下面的對象是否存在引用,
如果不存在,那么這個(gè)對象不會被標(biāo)記。再去檢查其他根。
2.壓縮階段
已經(jīng)標(biāo)記好的對象是可達(dá)的對象,未標(biāo)記的對象是不可達(dá)的,也就是垃圾對象。
現(xiàn)在開始第二階段:壓縮階段。
這個(gè)階段中,垃圾回收器會線性的遍歷堆,尋找未標(biāo)記對象的連續(xù)內(nèi)存塊。
如果內(nèi)存塊比較小,垃圾回收器會直接無視他們,忽略掉。
如果比較大,垃圾回收器會把非垃圾的對象移動到這里以壓縮堆。
垃圾回收會造成顯著的性能損失!這是托管堆的主要缺點(diǎn)。垃圾回收只在第0代滿的時(shí)候才發(fā)生。
|
垃圾回收與調(diào)試 |
只要對象變得不可達(dá),就會成為垃圾回收器的目標(biāo)!不保證對象在方法的生存周期中始終存活。
static void Main()
{
Timer t = new Timer(TimerCallBack, null, 0, 2000);
Console.Read();
}
private static void TimerCallBack(object obj)
{
Console.WriteLine("TimerCallBack:{0}", DateTime.Now);
GC.Collect();
}
上面的程序一般認(rèn)為會每2秒輸出一次,這里主動調(diào)用了GC回收方法。但是運(yùn)行的時(shí)候發(fā)現(xiàn)回調(diào)方法只被調(diào)用了一次。
因?yàn)檫@里t初始化之后,垃圾回收器發(fā)現(xiàn)在main方法里面再也沒有用過t。所以調(diào)用了一次回調(diào)方法以后t就被回收了!
有些開發(fā)人員嘗試這樣去解決
Timer t = new Timer(TimerCallBack, null, 0, 2000);
Console.Read();
t = null;
但是發(fā)現(xiàn)最后運(yùn)行的情況和上面一樣!
因?yàn)閷⒕植孔兞吭O(shè)為null,等于沒有引用這個(gè)變量。所以垃圾回收器一樣的回收你!
正確的解決方法
Timer t = new Timer(TimerCallBack, null, 0, 2000);
Console.Read();
t.Dispose();
因?yàn)閷ο蟊仨毚婊畈拍苷{(diào)用Dispose方法!
---------------------------------------------------------------------
Finalize和Safehandle就直接先忽略。以后接觸到再看!
唉!手動監(jiān)視和控制對象的生存期也先忽略吧!AppDomain和com操作不太熟悉,以后再看。
---------------------------------------------------------------------
|
代 |
代是CLR垃圾回收采用的一種機(jī)制,它的目的就是提升應(yīng)用程序的性能。
第0代
托管堆在初始化時(shí)不包含任何對象,添加到堆的對象稱為第0代對象。
第1代
當(dāng)程序不斷運(yùn)行,不斷有很多對象添加到托管堆,那么第0代預(yù)算的容量滿了,那么GC就會開始一次垃圾回收,那些0代中不可達(dá)的對象就會被回收。
在垃圾回收中存活的對象就會升級為第1代。
第2代
當(dāng)?shù)?代中的對象越來越多,垃圾回收器就會考慮回收第0代和第1代中的垃圾對象,這次回收以后,存活的第1代對象就會升級到第2代。
下面寫個(gè)程序演示代
Console.WriteLine("當(dāng)前系統(tǒng)支持的最大代:{0}", GC.MaxGeneration);
//在堆中創(chuàng)建一個(gè)新的GenObj
Object obj = new object();
Console.WriteLine("obj Gen:{0}", GC.GetGeneration(obj));//0
GC.Collect();
Console.WriteLine("obj Gen:{0}", GC.GetGeneration(obj));//1
GC.Collect();
Console.WriteLine("obj Gen:{0}", GC.GetGeneration(obj));//2
GC.Collect();
Console.WriteLine("obj Gen:{0}", GC.GetGeneration(obj));//2(不能再大了)
obj = null;

浙公網(wǎng)安備 33010602011771號