程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Finalizer線程對Object生命周期的影響

Finalizer線程對Object生命周期的影響

編輯:關於.NET

這期博客的話題有些沉重,我們來討論.net對象的生生死死。首先,要給生死下個定義。在這篇博客 中,每當談及一個對象是死了的對象,指的是用戶無法再獲得其引用。這個定義是個對用戶友好的定義 ,因為有很多時候,對象還殘存在托管堆上,CLR依舊可以通過一些手法來獲得它(比如RCW緩存中通過 SyncBlk),但是這種“生不如死”的狀態不在今天的討論范圍之內。

言歸正傳。眾所周知,.NET倚仗GC管理分配在托管堆上的對象(也就是new出來的東東)。為了提供類 似c++中析構函數的功能,也就是在對象即將死去的時候,執行一段用戶代碼來做一些清理工作,比如在 一個COM組件上調用它的Release方法。

出於性能的考慮,CLR使用一個獨立的線程來執行對象的Finalize方法,所以Finalize方法的執行並 不是GC.Collect的一部分。下面一個程序驗證了這個說法。

using System;
using System.Threading;
class ObjectWithFinalizer
{
    ~ObjectWithFinalizer()
    {
        Thread.Sleep(1000);
        Console.WriteLine("Finalize in thread {0}", Thread.CurrentThread.ManagedThreadId);
    }
}

class Program
{
    public static void Main()
    {
        Console.WriteLine("Run in thread {0}", Thread.CurrentThread.ManagedThreadId);
        ObjectWithFinalizer owf = new ObjectWithFinalizer();
        GC.Collect();
        Console.WriteLine("GC.Collect() end");
    }
}

程序的運行結果是

Run in thread 1
GC.Collect() end
Finalize in thread 2

對CLR的行為略作解釋。當GC發生的時候,CLR會遍歷每個線程(也就是在每個線程上執行GC的相關算 法),找出在當前執行點之後再也沒有被引用的Object,把他們視為死了的對象。然後,對那些有 finalize方法的對象,把他們放到專門用來執行Finalize方法的線程(我們稱為Finalizer線程),並逐 一執行之Finalize方法。這裡要注意的是,GCCollect方法和Finalize線程的執行並不是同一個概念。其 聯系是:GC驅使了Finalize線程的執行。然而,GC的結束並不意味著Finalize階段的結束。所以如果要 同步主線程和Finalzer線程的執行,我們要一個專門的API,GC.WaitForFinalization(下面會有一個例 子)

好奇的讀者可能會問,假設GC結束的時候,有一個"死"了的Object的Finalize方法還沒有被調用,那 麼他到底是死了還是活著?答案是,他是活者的,因為按照一開始給出的定義,在Finalizer線程中仍然 可以引用到這個object。為了驗證這個說法,我們用WeakReference來觀察。WeakReference是一類特別 的引用,普通的引用可以延長object的生命周期,而WeakReference則不能。舉一個例子來說。

using System;
using System.Threading;
class ObjectWithFinalizer
{
    int m_int;
    public ObjectWithFinalizer(int i)
    {
        m_int = i;
    }
    ~ObjectWithFinalizer()
    {
        Thread.Sleep(1000);
        Console.WriteLine("owf {0} is finalized", m_int);
    }
}

class Program
{
    public static void Main()
    {
        ObjectWithFinalizer owf = new ObjectWithFinalizer(1);
        ObjectWithFinalizer owf2 = new ObjectWithFinalizer(2);
        WeakReference wr = new WeakReference(owf);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Finalize phase is ended");
        Console.WriteLine(owf2);
        Console.WriteLine(wr);
    }
}

程序的執行結果是

owf 1 is finalized
Finalize phase is ended
ObjectWithFinalizer
System.WeakReference
owf 2 is finalized

GC.Collect()被調用的下方,owf2和wr都被引用。但是當GC及其引發的Finalize線程結束的時候, owf已經死了,說明wr對其的引用並不能延續它的生命。當一個object死去之後,WeakReference會被自 動設置成null,這個行為使得它成為我們探索對象生命周期時絕佳的跟蹤器。

在給出跟蹤器的例子之前,最後要介紹的是兩種類型的WeakReference:第一類是Short WeakReference,它不能跟蹤到finalizer線程裡的對象,也就是說當GC把一個對象放到Finalizer線程的 時候,它就已經被置null了;可以跟蹤到的稱之為long weakReference。對於同一個對象的引用,long weakReference的跟蹤范圍比short WeakReference更長。產生short/long weakReference的方法在於構 造函數裡的一個參數WeakReference(Object o,bool b),當b為true時,產生long WeakReference,否則 產生Short WeakReference。默認是false。

好了,下面的這段代碼是這篇博客的精華,先描述一下整體思路,具體解釋參見代碼中的注釋。 我 們創建了三個不同類型的對象,並且通過跟蹤器觀察在GC.Collect, GC.WaitForPendingFinalizer前後 他們是死是活。

using System;
using System.Threading;
using System.Collections.Generic;

/// <summary>
/// 該類型為每一個Object方法創建了ShortWeakReference, LongWeakReference
/// 並記錄了object.ToString(),方便顯示
/// </summary>
class ObjectRecord
{
    string m_objString;
    WeakReference m_shortWeakReference;
    WeakReference m_longWeakReference;

    public ObjectRecord(Object o)
    {
        m_objString = o.ToString();
        m_longWeakReference = new WeakReference(o, true);
        m_shortWeakReference = new WeakReference(o, false);
    }

    public override string ToString()
    {
        return m_objString;
    }

    public Object ObjectByShortWeakReference
    {
        get { return m_shortWeakReference.Target; }
    }

    public Object ObjectByLongWeakReference
    {
        get { return m_longWeakReference.Target; }
    }
}

/// <summary>
/// 對象跟蹤器
/// </summary>
class ObjectTracker
{
    List<ObjectRecord> m_lstObjectRecord;

    public ObjectTracker()
    {
        m_lstObjectRecord = new List<ObjectRecord>();
    }

    /// <summary>
    /// 注冊一個對象到跟蹤器中
    /// </summary>
    /// <param name="o"></param>
    public void Register(Object o)
    {
        m_lstObjectRecord.Add(new ObjectRecord(o));
    }

    /// <summary>
    /// 顯示注冊到跟蹤器的對象的生死:)
    /// </summary>
    public void ShowObjects()
    {
        foreach (ObjectRecord obj in m_lstObjectRecord)
        {
            Console.Write(obj);
            if (obj.ObjectByShortWeakReference != null)
            {
                // 如果ShortWeakReference能引用到,那麼對象是活的
                Console.WriteLine(" is live", obj);
            }
            else if (obj.ObjectByLongWeakReference != null)
            {
                // 只由LongWeakReference能引用到,那麼對象只存活在Finalzer 線程中
                Console.WriteLine(" is live in finalizer", obj);
            }
            else
            {
                // WeakRefence都引用不到,那麼對象死了
                Console.WriteLine(" is a dead Object");
            }
        }
    }
}

class ObjectWithFinalizer
{
    Object m_obj;
    public ObjectWithFinalizer(Object obj)
    {
        m_obj = obj;
    }

    ~ObjectWithFinalizer()
    {
        Thread.Sleep(1000);
        ConsoleColor bkConsoleColor = Console.ForegroundColor;
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine("Finalized in thread {0}", Thread.CurrentThread.ManagedThreadId);
        Console.ForegroundColor = bkConsoleColor;
    }
}

class Program
{
    public static void Main()
    {
        // 1. 創建 3 個對象
        Program p = new Program();
        Object o = new Object();
        ObjectWithFinalizer owf = new ObjectWithFinalizer(o);

        // 2. 創建跟蹤器,並把這三個對象注冊到跟蹤器中
        ObjectTracker objectTracker = new ObjectTracker();
        objectTracker.Register(p);
        objectTracker.Register(o);
        objectTracker.Register(owf);

        // 3. 第一次GC
        GC.Collect();
        objectTracker.ShowObjects();
        // 輸出:
        // Program is a dead Object
        // System.Object is live in finalizer
        // ObjectWithFinalizer is live in finalizer

        // 4. 等待Finalizer線程完成工作
        GC.WaitForPendingFinalizers();
        objectTracker.ShowObjects();
        // 輸出:
        // Finalized in thread 2
        // Program is a dead Object
        // System.Object is live in finalizer
        // ObjectWithFinalizer is live in finalizer

        // 5. 再度GC
        GC.Collect();
        objectTracker.ShowObjects();
        // 輸出:
        // Program is a dead Object
        // System.Object is a dead Object
        // ObjectWithFinalizer is a dead Object
    }

}

值得一說的是,當Finalize線程結束工作的時候,並不會把那些Long WeakReference置null,所以仍 然發現兩個Object在Finalizer裡面是活的,必須等到再一次GC,才能把它們收集。

那麼,如果在第二次GC之前,把這個WeakReference重新賦給一個普通的對象,會發生什麼事情呢? 下一個例子給出了解釋:

using System;

class ObjectFromHell
{
    public void HelloWorld()
    {
        Console.WriteLine("Hello World!");
    }
    ~ObjectFromHell()
    {
        Console.WriteLine("Hello Hell:)");
    }
}

class Program
{
    public static void Main()
    {
        ObjectFromHell ofh = new ObjectFromHell();
        WeakReference wr = new WeakReference(ofh, true);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        ObjectFromHell ofh2 = wr.Target as ObjectFromHell;
        if (ofh2 != null)
            ofh2.HelloWorld();
    }
}

輸出結果如下:

Hello Hell:)
Hello World!

在這個例子中,ObjectFromHell已經被Finalize了,但是我們依然可以通過LongWeakReference來使 他復生(Resurrect)。(強烈不推薦使用這種方法來操縱Object。。。)

總結一下今天講的東西:

1. GC把有Finalize方法的對象放到一個單獨的Finalizer線程中執行他們的Finalize方法。

2. 在主程序中如果要等待Finalizer線程結束,需要顯示調用GC.WaitForPendingFinalizer方法。

3. 之後需要再調用一次GC,才能把Finalizer線程裡面的垃圾收集。

4. 用WeakReference可以跟蹤對象,shortWeakRefence跟蹤到Finalize之前,LongWeakReference跟 蹤到Finalize裡面。

5. 使用LongWeakReference可以使對象復生。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved