我想寫一個演示程序來演示它的區別。結果比我指望的更具挑戰性。第一個必要的組件是確保終結器線程可以放慢速度,這樣您就可以觀察到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,直到其終結者完成。
當對象被複活時,要注意一個怪癖,當重新創建一個強引用時,從可擴展隊列移回正常堆。不是我在這個演示程序中探索的東西,但需要長時間的弱參考才能觀察到這一點。你需要長時間弱點的基本原因。
哇,這是令人困惑的文檔...它看起來像簡短的參考文檔應該談論定稿而不是垃圾收集 - 但目前尚不清楚。 – 2015-03-19 07:52:36
我仔細查看了舊的SSCLI源代碼,試圖找到我能找到的東西。目前還沒有發現任何確定的內容,但是在Target的getter中找到了一個有趣的評論:「只有在非法使用時纔會發生,比如使用來自終結器的WeakReference」 - 所以也許你試圖做的是註定要失敗。 – 2015-03-19 08:07:55
不是一個答案,但值得注意的是,你通常應該避免析構函數,這很可能是一個XY問題。 – 2015-03-19 11:50:29