国产美女一级毛片精品久久久|婷婷影院在线综合免费视频|最新国产午夜精品视频成人|久久精品九九无码免费

.NET中的弱引用

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

弱引用是什么?

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

1

Cat cat = new Cat()

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

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

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

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對象實例已經不存在了,此時weakReference.Target是null。

WeakReference類型還有一個構造函數的重載為:

1

Public WeakReference(Object target, bool trackResurrection)

其中bool類型的參數trackResurrection指定了這個WeakReference實例是一個長弱引用還是一個短弱引用。對于短弱引用,當它所指向的對象被垃圾回收機制標記為“不可達”狀態(tài)(即將被回收)時,該弱引用的Target屬性即為null。而對于長弱引用,當它所指向對象的析構函數被調用之后,它的Target屬性仍然是有效的。

弱引用的內部實現

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

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

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

WeakReference的一些注意事項

首先,WeakReference自身也實現了析構函數。也就是說,它即使不再被使用了,也不會被立即回收,而是會在內存里賴著多活一會(可能會經歷不止一次的垃圾回收)。

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

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

WeakReference的三個使用場景

對象緩存

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

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

改進措施很簡單——使用完位圖對象后,不是直接丟棄,而是用一個弱引用指向它。待下次訪問位圖對象時,就可以先通過弱引用判斷位圖對象是否還在內存中,如果還在則直接使用,否則重新創(chuà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方法可以得到內存中A類型的實例個數。另外,還可以通過instances集合來檢查內存中的A類型實例都有哪些。在調試內存泄露問題的時候,這些信息都可以派上用場。

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

弱事件

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

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

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

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

熱門話題

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

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

Copyright © 2006 | 湖南大計信息科技有限公司 版權所有

湘ICP備14017520號-3

關注我們
在線咨詢
嘿,我來幫您!