2015-03-19 54 views
8

我使用我的課Foo中的WeakReference<T>short weak reference)跟蹤對象。這個類有一個析構函數,我需要訪問該跟蹤對象。我跟蹤的對象還使用WeakReference<Foo>跟蹤Foo短的弱引用何時變爲空?

所以現在我想知道什麼時候WeakReference發生了「調零」?是否所有WeakReference在ANY終結器運行之前都會被清空,或者在它們跟蹤的對象的終結器即將運行之前,它們中的每一個都會被清零?

UPDATE

現在我也想知道,也許Mono項目可以在這一個(link 1link 2)一些啓發。但我有點擔心MS GCMono GC可能會以不同的方式處理此問題,並且不兼容

+0

哇,這是令人困惑的文檔...它看起來像簡短的參考文檔應該談論定稿而不是垃圾收集 - 但目前尚不清楚。 – 2015-03-19 07:52:36

+0

我仔細查看了舊的SSCLI源代碼,試圖找到我能找到的東西。目前還沒有發現任何確定的內容,但是在Target的getter中找到了一個有趣的評論:「只有在非法使用時纔會發生,比如使用來自終結器的WeakReference」 - 所以也許你試圖做的是註定要失敗。 – 2015-03-19 08:07:55

+1

不是一個答案,但值得注意的是,你通常應該避免析構函數,這很可能是一個XY問題。 – 2015-03-19 11:50:29

回答

4

您可以使用簡單的測試程序自行驗證。但我發現WeakReference類型的文檔比您所看到的頁面更清晰。

特別是,您鏈接的頁面中被稱爲「短」和「長」的標記在the actual constructor documentation中被稱爲trackResurrection。並且參數說明如下:

指示何時停止跟蹤對象。如果爲true,則在完成後跟蹤對象;如果爲false,則僅在完成之前跟蹤對象。

的 「備註」 部分也記載:

如果trackResurrection是假的,則創建短弱引用。如果trackResurrection爲true,則創建一個長的弱參考。

這證實,當你使用一個「短」弱引用,一個敲定的對象將不再跟蹤由WeakReference對象(即Target變得null),但是當你用「長」弱引用,這將是。

對於這兩種弱引用,實際上被垃圾收集的對象肯定不會被跟蹤(顯然)。

通常,程序中沒有其他線程應該能夠在終結器線程實際執行其工作時觀察對象,因此當Target屬性設置爲null時,「short」弱引用的精確時刻似乎向我開玩笑。如果程序中的某個其他線程將該值視爲非null,則終結器尚未運行。如果它將其視爲null,則終結器已運行。這個「其他線程」本身不應該在終結器線程工作的時候運行,所以最終確定本質上應該與「其他線程」有關。

5

我想寫一個演示程序來演示它的區別。結果比我指望的更具挑戰性。第一個必要的組件是確保終結器線程可以放慢速度,這樣您就可以觀察到WeakReference.IsAlive的價值,而不會冒受受終結器線程競爭影響的風險。所以我用:

class FinalizerDelayer { 
    ~FinalizerDelayer() { 
     Console.WriteLine("Delaying finalizer..."); 
     System.Threading.Thread.Sleep(500); 
     Console.WriteLine("Delay done"); 
    } 
} 

這時,一個小的類,這將是的WeakReference的目標:

class Example { 
    private int instance; 
    public Example(int instance) { this.instance = instance; } 
    ~Example() { 
     Console.WriteLine("Example {0} finalized", instance); 
    } 
} 

然後演示了一個長和短弱引用之間的區別的程序:

class Program { 
    static void Main(string[] args) { 
     var target1 = new Example(1); 
     var target2 = new Example(2); 
     var shortweak = new WeakReference(target1); 
     var longweak = new WeakReference(target2, true); 
     var delay = new FinalizerDelayer(); 
     GC.Collect();  // Kills short reference 
     Console.WriteLine("Short alive = {0}", shortweak.IsAlive); 
     Console.WriteLine("Long alive = {0}", longweak.IsAlive); 
     GC.WaitForPendingFinalizers(); 
     Console.WriteLine("Finalization done"); 
     GC.Collect();  // Kills long reference 
     Console.WriteLine("Long alive = {0}", longweak.IsAlive); 
     Console.ReadLine(); 
    } 
} 

您必須運行該程序,以便調試器不會影響對象的生命週期。選擇發佈版本並更改調試器設置:工具+選項,調試,常規,取消「抑制JIT優化」選項。

原來,確定對象的確切順序是非確定性的。每次運行該程序時,順序都不相同。我們希望FinalizerDelayer對象首先完成,但這並不總是會發生。 I 認爲這是內置地址空間佈局隨機化功能的副作用,它使託管代碼非常難以攻擊。但運行往往不夠,你最終會得到:

延遲終結...
短活着=假
龍活着=真
延遲完成
例1完成
例2敲定
定稿完成
龍活着=假

長話短說:

  • 短弱引用集作爲對象被收集並放置在freachable隊列儘快向的IsAlive假,準備爲它完成。該對象仍然物理存在,但不再有強有力的引用,它很快就會完成。
  • 長時間的弱引用會一直跟蹤對象,直至其真正的生活時間,包括它在可變長隊列中的生活。 IsAlive將不會被設置爲false,直到其終結者完成。

當對象被複活時,要注意一個怪癖,當重新創建一個強引用時,從可擴展隊列移回正常堆。不是我在這個演示程序中探索的東西,但需要長時間的弱參考才能觀察到這一點。你需要長時間弱點的基本原因。

0

這麼多的研究之後,我能夠找到這個古老的文章Garbage Collection Part 2: Automatic Memory Management in the Microsoft .NET Frameworkpart 1討論復活和freachable隊列):

現在,這裏當垃圾回收(GC)運行時會發生什麼:

  1. 垃圾回收器生成所有可到達對象的圖形。本文的第1部分討論了這是如何工作的。

  2. 垃圾收集器掃描簡短的弱引用表。如果表格中的指針指向不是圖表的一部分的對象,則指針標識不可到達的對象,並且中的插槽將短弱參考表格設置爲空

  3. 垃圾收集器掃描確定隊列。如果隊列中的指針指向不是圖的一部分的對象,則該指針將標識一個不可訪問的對象,並且該指針將從終結隊列移至可擴展隊列。此時,該對象被添加到圖中,因爲該對象現在被認爲是可達的。

  4. 垃圾回收器掃描較長的弱引用表。如果表中的指針指向不是圖的一部分的對象(它現在包含可擴展隊列中的條目所指向的對象),則該指針標識一個不可訪問的對象,並且該槽被設置爲空。

  5. 垃圾收集器壓縮內存,擠出無法訪問的對象留下的空洞。

所以,即使我的Foo類有一個終結,因此這將是一個freachable隊列(這被認爲是根) - 的短弱引用解決發生前該對象被根植於這意味着簡短的弱引用將爲空:

垃圾回收器一旦確定該對象不可訪問,就立即在短的弱引用表中將指針設置爲null。如果對象具有Finalize方法,該方法尚未被調用,所以對象仍然存在。如果應用程序訪問WeakReference對象的Target屬性,則即使該對象實際上仍然存在,也會返回null。


此外,在Yun Jin's WebLog如所提到的,這是一般不良終結引用終結對象,但WeakReference是有點異常的(雖然this was not always the case)。由於WeakReference是帶終結器的對象,因此如果在Foo的終結器中訪問該對象,它可能已經最終確定(在這種情況下,儘管事實上跟蹤對象仍然可以正常運行,但Target屬性將始終返回空值(more info) 。


我剛纔已經證實,Mono garbage collector與此行爲是一致的


有用的參考:
- WeakReference源代碼
- WeakReference<T>源代碼

+0

_「由於WeakReference是一個帶終結器的對象」_ - 你還確定你在這裏的正確軌道? – 2015-03-19 20:15:05

+0

@HenkHolterman它確實使事情複雜化了很多,但希望我能以某種方式解決這個問題。比如通過繼承WeakReference,並將所需的所有奇妙邏輯放在那裏。 :-) GC做的順序對於把它做對是至關重要的。 – Paya 2015-03-19 20:28:56