国产99福利精品视频|杨幂精品国产福利在线|国精品午夜福利视频不卡|欧美精品黄网站在线播放|精品国产乱码久久久久久久|国产大片中文字幕在线观看|国产肥熟女视频一区二区三区|国产乱码无码视频一区二区三区

.NET中的弱引用

來源:長沙北大青鳥大計校區(qū)|發(fā)布時間:2015-07-19 09:10:00

弱引用是什么?

要搞清楚什么是弱引用,我們需要先知道強引用是什么。強引用并不是什么深奧的概念,其實我們平時所使用的.Net引用就是強引用。例如:

1

Cat cat = new Cat()

變量cat就是一個強引用,它指向了堆中的一個Cat對象實例。我們都知道,CLR的垃圾回收機制會標(biāo)記所有被強引用到的對象,而那些剩下的未被標(biāo)記的對象則會被垃圾回收。換句話說,如果一個對象一直被某個強引用所指向,那么它是不會被垃圾回收的。

從這一點來看,弱引用就完全不一樣了——即使某個對象被弱引用所指向,該對象仍然會被垃圾回收。也就是說,弱引用不會影響對象的生命周期。

System.WeakReference類是.net為我們提供的一個弱引用的實現(xiàn),可以這么用:

1

2

3

4

5

6

7

8

9

10

WeakReference weakReference = new WeakReference(new Cat());

Cat strongReference = weakReference.Target as Cat;

if (strongReference != null)

{

   // Cat對象實例尚未被垃圾回收,可以通過strongReference進行訪問

}

else

{

   // Cat對象實例已被垃圾回收

}

如果在上例的第一行代碼之后第二行代碼之前,CLR發(fā)生了一次垃圾回收,那么可以基本斷定那個Cat對象實例已經(jīng)不存在了,此時weakReference.Target是null。

WeakReference類型還有一個構(gòu)造函數(shù)的重載為:

1

Public WeakReference(Object target, bool trackResurrection)

其中bool類型的參數(shù)trackResurrection指定了這個WeakReference實例是一個長弱引用還是一個短弱引用。對于短弱引用,當(dāng)它所指向的對象被垃圾回收機制標(biāo)記為“不可達”狀態(tài)(即將被回收)時,該弱引用的Target屬性即為null。而對于長弱引用,當(dāng)它所指向?qū)ο蟮奈鰳?gòu)函數(shù)被調(diào)用之后,它的Target屬性仍然是有效的。

弱引用的內(nèi)部實現(xiàn)

弱引用看起來很神奇,似乎是凌駕于正常的垃圾回收機制之上的,它究竟是如何實現(xiàn)的呢?其實WeakReference類型在內(nèi)部封裝了一個名為GCHandle的struct類型,正是這個GCHandle使弱引用成為可能。

CLR中的每個AppDomain都擁有一個GC句柄表。這個表的每一項記錄有兩個信息,一個是指向堆中某個對象的指針,另一個是這個表項的類型。總共有4種表項類型,其中Weak和WeakTrackResurrection兩種類型和我們今天所討論的弱引用相關(guān)。GCHandle這個類提供了一些操縱GC句柄表的方法。我們可以使用它的Alloc方法向GC句柄表中添加一個指定類型的表項。當(dāng)垃圾回收開始后,垃圾回收器找到所有可達對象(簡單的說,就是有用的對象)。然后遍歷GC句柄表中每個Weak類型的表項,如果發(fā)現(xiàn)某表項所指的對象不屬于可達對象,則會把該表項的對象指針設(shè)置為null。緊接著,垃圾回收器會找出所有不可達對象中定義了析構(gòu)函數(shù)的對象,并把他們放到一個被稱為freachable的隊列中(freachable中的對象會等待一個CLR中特定的線程來調(diào)用他們的終結(jié)函數(shù))。由于這些freachable中的對象現(xiàn)在又被freachable隊列所引用,所以它們又成為可達對象了。此時,垃圾回收器會遍歷GC句柄表中所有WeakTrackResurrection類型的表項,和剛才一樣,如果某表項所指的對象不屬于可達對象,則會把該表項的對象指針設(shè)置為null。此處需注意,對于那些一開始被判定為不可達且定義了析構(gòu)函數(shù)的對象來說,它們在GC句柄表中所對于的表項指針仍然不是null。這就是Weak和WeakTrackResurrection兩種類型的區(qū)別。

WeakReference就是通過表示了某個GC句柄表表項的GCHandle對象來完成跟蹤對象生命周期的功能的。你也一定可以看出短弱引用利用了Weak類型的GC句柄表項,而長弱引用則利用了WeakTrackResurrection類型的表項。

WeakReference的一些注意事項

首先,WeakReference自身也實現(xiàn)了析構(gòu)函數(shù)。也就是說,它即使不再被使用了,也不會被立即回收,而是會在內(nèi)存里賴著多活一會(可能會經(jīng)歷不止一次的垃圾回收)。

另外,如上一節(jié)所說,WeakReference會向GC句柄表添加一個表項。而每次垃圾回收,GC句柄表都會被遍歷一遍?上攵绻到y(tǒng)中存在大量的WeakReference,那么GC句柄表很可能也會非常龐大,導(dǎo)致垃圾回收的效率降低。

WeakReference經(jīng)常會和緩存聯(lián)系起來,但是它并不適和用來實現(xiàn)一個大型的緩存機制。這是為什么呢?一方面如前文所述,WeakReference自身實現(xiàn)了析構(gòu)函數(shù),也有可能導(dǎo)致垃圾回收的效率降低,因此應(yīng)該避免在內(nèi)存中創(chuàng)建大量的WeakReference對象實例。另一方面,我們知道一個對象如果沒有被任何強引用所指向,而僅僅被弱引用所指向,那么它很有可能活不過一次垃圾回收。所以通過這樣的方式所實現(xiàn)出來的緩存機制勢必有著非常短促的緩存策略,而這種策略在大部分情況下都不會是你期望得到的。

WeakReference的三個使用場景

對象緩存

試想這樣一個場景,我有一個內(nèi)存受限的程序,在這個程序里經(jīng)常會使用一個占用很多內(nèi)存的位圖對象,所幸生成這個位圖對象并不復(fù)雜。所以我每次要使用那個位圖對象的時候都會重新生成它,使用完畢之后就將其丟棄(不保留它的引用)。

這種方式完全能夠滿足我的需求,但是還能不能再優(yōu)化呢?分析一下我們就可以發(fā)現(xiàn),當(dāng)我需要使用位圖對象的時候,我上次使用的那個位圖對象雖然被我丟棄了,但可能仍然沒有被垃圾回收,仍然存在內(nèi)存中。此時如果我能直接使用這個位圖對象,就可以節(jié)省出因重建位圖對象而浪費的內(nèi)存和CPU資源。

改進措施很簡單——使用完位圖對象后,不是直接丟棄,而是用一個弱引用指向它。待下次訪問位圖對象時,就可以先通過弱引用判斷位圖對象是否還在內(nèi)存中,如果還在則直接使用,否則重新創(chuàng)建。

輔助調(diào)試

有時,對于某種類型,我們需要知道當(dāng)前程序中存在有多少對象實例,以及存在的都是哪些實例,以便于我們進行一些性能分析。這時,我們就可以使用到弱引用了。例如下面的代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class A

{

   private static List<WeakReference> _instances = newList<WeakReference>();

 

   public A()

   {

       _instances.Add(new WeakReference(this));

   }

 

   public static int GetInstanceCount()

   {

       GC.Collect();

       return _instances.Count(x => x.Target != null);

   }

}

GetInstanceCount方法可以得到內(nèi)存中A類型的實例個數(shù)。另外,還可以通過instances集合來檢查內(nèi)存中的A類型實例都有哪些。在調(diào)試內(nèi)存泄露問題的時候,這些信息都可以派上用場。

相比于各種性能分析Profiler工具,這種方法更加輕巧便捷。

弱事件

.Net中的事件有時會引起內(nèi)存泄露問題。例如,A注冊了B的某個事件,此時B就會暗中保留A的一個強引用,導(dǎo)致A無法被內(nèi)存回收,直到B被回收或 A反注冊了B的事件。例如,我有一個對象注冊了主窗口的Loaded事件,只要我不反注冊該事件,那么主窗口會一直引用該對象,直到主窗口被關(guān)閉,該對象才會被回收。所以,每當(dāng)我們注冊某個對象的事件時,都有可能在不經(jīng)意間埋下內(nèi)存泄露的隱患。

解決這個問題的根本方法是,在必要的時候進行事件的反注冊。但是,在某些情況下,我們可能很難判定這個“必要的時候”。另外,當(dāng)我們作為類庫的提供者時,我們也很難保證類庫的使用者都記得要反注冊事件。因此,另一個解決方案就是使用弱事件。

弱事件的實現(xiàn)原理很簡單,就是對事件進行一層封裝。不讓事件發(fā)布者直接引用監(jiān)聽者,而是讓他們保留一個監(jiān)聽者的弱引用。當(dāng)事件觸發(fā)時,發(fā)布者會先檢查監(jiān)聽者是否還存在于內(nèi)存中,如果存在才通知它。如此一來,監(jiān)聽者的生命周期就不會依賴于發(fā)布者了。

上一篇:.Net框架的組成
下一篇:ASP 五大高效提速技巧

熱門話題

招生熱線: 4008-0731-86 / 0731-82186801

學(xué)校地址: 長沙市天心區(qū)團結(jié)路6號

Copyright © 2006 | 湖南大計信息科技有限公司 版權(quán)所有

湘ICP備14017520號-3

關(guān)注我們
在線咨詢
嘿,我來幫您!