首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Finalizer 執行緒對物件生命週期的影響深度解析

     編輯:關於.NET

.NET 物件銷毀過程涉及 GC 執行緒與 Finalizer 執行緒的協作。本文透過代碼實驗驗證 Finalizer 執行緒行為、WeakReference 追蹤物件存活狀態、物件復活現象與最佳實踐。

核心觀察:Finalizer 非同步執行

class ObjectWithFinalizer {
    ~ObjectWithFinalizer() {
        Thread.Sleep(1000);
        Console.WriteLine("Finalize in thread {0}", Thread.CurrentThread.ManagedThreadId);
    }
}
static void Main() {
    Console.WriteLine("Run in thread {0}", Thread.CurrentThread.ManagedThreadId);
    new ObjectWithFinalizer();
    GC.Collect();
    Console.WriteLine("GC.Collect() end");
}
// 輸出:
// Run in thread 1
// GC.Collect() end
// Finalize in thread 2  <-- 獨立執行緒
  • GC 掃描各執行緒堆疊,標記不可達物件。
  • 具 Finalize 方法的物件放入 Finalizer 隊列,由專屬執行緒逐一執行。
  • GC.Collect() 返回不代表 Finalize 完成;需呼叫 GC.WaitForPendingFinalizers() 同步等待。

WeakReference 追蹤生命週期

WeakReference 不延長物件壽命,分兩種:

  • Short WeakReference (預設):GC 將物件移入 Finalizer 隊列時即設為 null。
  • Long WeakReference (new WeakReference(obj, true)):可追蹤至 Finalizer 執行期間。

完整追蹤實驗

class ObjectTracker {
    List records = new List();
    public void Register(object o) => records.Add(new ObjectRecord(o));
    public void ShowObjects() {
        foreach (var r in records) {
            Console.Write(r);
            if (r.ShortTarget != null) Console.WriteLine(" is live");
            else if (r.LongTarget != null) Console.WriteLine(" is live in finalizer");
            else Console.WriteLine(" is a dead Object");
        }
    }
}
class ObjectRecord {
    string desc; WeakReference shortRef, longRef;
    public ObjectRecord(object o) { desc=o.ToString(); shortRef=new WeakReference(o,false); longRef=new WeakReference(o,true); }
    public object ShortTarget => shortRef.Target;
    public object LongTarget => longRef.Target;
    public override string ToString() => desc;
}

static void Main() {
    var tracker = new ObjectTracker();
    var p = new Program();
    var o = new object();
    var owf = new ObjectWithFinalizer(o);
    tracker.Register(p); tracker.Register(o); tracker.Register(owf);

    GC.Collect(); tracker.ShowObjects();
    // Program is a dead Object
    // System.Object is live in finalizer
    // ObjectWithFinalizer is live in finalizer

    GC.WaitForPendingFinalizers(); tracker.ShowObjects();
    // Finalized in thread 2
    // Program is a dead Object
    // System.Object is live in finalizer
    // ObjectWithFinalizer is live in finalizer

    GC.Collect(); tracker.ShowObjects();
    // Program is a dead Object
    // System.Object is a dead Object
    // ObjectWithFinalizer is a dead Object
}

物件復活

Finalize 完成前,透過 Long WeakReference 取得引用並賦值給強引用,物件可「復活」:

var ofh = new ObjectFromHell();
var wr = new WeakReference(ofh, true);
GC.Collect(); GC.WaitForPendingFinalizers();
var ofh2 = wr.Target as ObjectFromHell;
if (ofh2 != null) ofh2.HelloWorld();
// 輸出:
// Hello Hell:)
// Hello World!

強烈不建議 使用復活技術,會破壞 GC 假設、導致非預期行為。

總結重點

  1. Finalize 方法在專屬 Finalizer 執行緒執行,非 GC.Collect() 同步部分。
  2. 主程式需呼叫 GC.WaitForPendingFinalizers() 等待 Finalize 完成。
  3. Finalize 結束後需第二次 GC 才能回收該記憶體。
  4. Short WeakReference 追蹤至 Finalize 前;Long WeakReference 追蹤至 Finalize 中。
  5. Long WeakReference 可實現物件復活,但屬反模式,嚴禁生產代碼使用。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved