【转】.net框架读书笔记---CLR内存管理\垃圾收集(四)
弱引用
當一個根指向一個對象時,該對象不可能被垃圾收集器收集,在這種情況下,通常說存在一個該對象的強引用(strong reference)。垃圾收集器還支持弱引用(weak reference)的概念。弱引用允許垃圾收集器收集對象,同時也允許應用程序訪問該對象,結果取決于時間。
如果對象的弱引用存在,那么在垃圾收集執行后,該對象的內存將被執行清理。另一方面,要訪問一個弱引用對象,應用程序必須獲取該對象的一個強引用。如果應用程序在對象被執行之前得到了它的強引用,那么垃圾收集器將不再對該對象執行垃圾收集,因為這時存在一個該對象的強引用。
下面代碼展示了弱引用的使用
class?Program
{
static?void?Main(?string?[] args)
{
SomeWeakReferenceMethod();
SomeStrongtReferenceMethod();
}
static?void?SomeWeakReferenceMethod()
{
//?創建一個強引用對象
SomeClass o?=?new?SomeClass();
WeakReference wr?=?new?WeakReference(o);
o?=?null?;?//?移除對象的強引用
o?=?wr.Target?as?SomeClass;
if?(o?==?null?)
{
//?出現過垃圾回收,對象的內存已經被回收
Console.WriteLine(?"?已經被回收?"?);
}
else
{
//?未出現垃圾回收
Console.WriteLine(?"?沒有被回收?"?+?o.X.ToString());
}
}
static?void?SomeStrongtReferenceMethod()
{
//?創建一個強引用對象
SomeClass o?=?new?SomeClass();
o?=?null?;?//?移除對象的強引用
try
{
Console.WriteLine(o.X);
}
catch
{
Console.WriteLine(?"?error?"?);
}
}
}
public?class?SomeClass
{
private?int?x;
public?int?X
{
get
{
return?this?.x;
}
}
public?SomeClass()
{
x?=?5?;
}
}
個人感覺弱引用機制有點像緩存,將一些操作起來比較費時(比如便利系統的硬盤)的東西第一次獲取之后暫時放入緩存中,并且將其引用置為null來減少應用程序的壓力,但是放置在緩存中可以再次使用(減少讀取時的壓力),至于是否能夠再次使用成功要看垃圾收集器。如果執行過垃圾收集,弱引用的對象將會被回收掉當然就無法再次使用了。
System.WeakReference有兩個公用構造器:
- public WeakReference(object target);
- ?public WeakReference(object target, bool trackResurrection);
target表示要追蹤的對象(上例中為SomeClass), trackResurrection表示是否要追蹤對象的復蘇,換句話說就是對象在執行Finalize方法后,是否還要追蹤該對象(對象執行Finlize后對象復蘇了) 。
將不追蹤對象復蘇的WeakReference稱為短弱引用(short weak reference),而將追蹤對象復蘇的稱為長弱引用(long weak reference)。如果一個對象沒有Finalize方法,長短弱引用是一樣的,最好不要使用長弱引用,因為長弱引用在一個對象被執行終止后允許該對象復蘇,將會導致對象的狀態不可預知。
一旦創建了對象的弱引用,通常將該對象的強引用設置為null,為了再次使用該對象,需要將弱引用轉換為一個強引用,通過WeakReference的Target屬性來完成的。如果target為null,那么對象已經被執行了垃圾收集,否則將會得到該對象的強引用。這時對象將不會被垃圾收集器收集了。
弱引用的內部機理
需要再次探究托管堆。托管堆中包含了兩個內部數據結構來管理弱引用。即短弱引用表和長弱引用表。這兩個表包含著一些指針,他們引用著托管堆對象。
當創建一個WeakReference對象時,它會在兩個弱引用表中選擇一個(長短弱引用相應對應),并在其中尋找一個空白插槽。該插槽的值將被設為我們希望追蹤對象的地址---也就是new WeakReference構造的那個對象的地址,弱引用表將不會認為是應用程序的根,否則垃圾收集器將不能收集它們中的指針引用的對象。
垃圾收集器運行時發生的一系列事情:
- 垃圾收集器構造一個可達對象的圖(.net框架讀書筆記---CLR內存管理\垃圾收集(一));
- 垃圾收集器掃描短弱引用表,如果表中的對象不是前面可達對象圖的一部分(對象已經不是根,在上面的例子中o已經被置為null了),那么表示該對象是一個不可達的對象,將短弱引用表中對應插槽的值將被設置為null(Target為null了);
- 垃圾收集器掃描終止化鏈表(.net框架讀書筆記---CLR內存管理\垃圾收集(二))。如果該鏈表中指針引用的對象不是可達對象圖的一部分,那么該對象將是不可達對象,它將被從終止化鏈表轉移到終止化可達隊列上。這時對象由成為可達對象圖的一部分了。
- 垃圾收集器掃描長弱引用表,如果表中的對象不是前面可達對象圖的一部分(該圖現在已經包括終止化可達隊列中引用的對象了),那么表示該對象是一個不可達的對象,將長弱引用表中對應插槽的值將被設置為null(Target為null了);
- 垃圾收集器壓縮內存,填充不可達對象空出的位置。
繼續分析代碼
if?(o?==?null?)
{
//?出現過垃圾回收,對象的內存已經被回收
Console.WriteLine(?"?已經被回收?"?);
}
else
{
//?未出現垃圾回收
Console.WriteLine(?"?沒有被回收?"?+?o.X.ToString());
}
因為在此之前o已經被置為null了,所以它已經是一個不可達對象,如果執行垃圾收集器,o自然不在可達對象圖中,那么垃圾收集器將會將短弱引用表中插槽對應的值設為null,這樣Target將返回null,上面代碼自然會執行if里面的代碼。如果垃圾收集器還沒有執行,雖然o已經為null,但是短弱引用插槽依然保存著對象的引用,那么Target將會返回對象的引用,使對象繼續可達,可以使用,當然上面代碼就會執行else里面的代碼。
短弱引用并不追蹤對象的復蘇。只要垃圾收集器判斷對象成為不可達的對象,它就會把短弱引用表中對應的指針設置為null。如果對象重寫了Finalize方法,那么這時該方法還沒有被調用,所以對象仍然存在。此時target仍然返回null,雖然這時對象已經進入終止化可達隊列,對象仍然存在。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【转】.net框架读书笔记---CLR内存管理\垃圾收集(四)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信用卡积分可以兑换现金吗
- 下一篇: 【转】1.5 运行原理剖析