.NET 物件銷毀過程涉及 GC 執行緒與 Finalizer 執行緒的協作。本文透過代碼實驗驗證 Finalizer 執行緒行為、WeakReference 追蹤物件存活狀態、物件復活現象與最佳實踐。
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.Collect() 返回不代表 Finalize 完成;需呼叫 GC.WaitForPendingFinalizers() 同步等待。WeakReference 不延長物件壽命,分兩種:
null。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 假設、導致非預期行為。
GC.Collect() 同步部分。GC.WaitForPendingFinalizers() 等待 Finalize 完成。