Finalizer线程对Object生命周期的影响

这期博客的话题有些沉重,我们来讨论.net对象的生生死死。首先,要给生死下个定义。在这篇博客中,每当谈及一个对象是死了的对象,指的是用户无法再获得其引用。这个定义是个对用户友好的定义,因为有很多时候,对象还残存在托管堆上,CLR依旧可以通过一些手法来获得它(比如RCW缓存中通过SyncBlk),但是这种“生不如死”的状态不在今天的讨论范围之内。

言归正传。众所周知,.NET倚仗GC管理分配在托管堆上的对象(也就是new出来的东东)。为了提供类似c++中析构函数的功能,也就是在对象即将死去的时候,执行一段用户代码来做一些清理工作,比如在一个COM组件上调用它的Release方法。

出于性能的考虑,CLR使用一个独立的线程来执行对象的Finalize方法,所以Finalize方法的执行并不是GC.Collect的一部分。下面一个程序验证了这个说法。

Code

程序的运行结果是

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则不能。举一个例子来说。

Code

程序的执行结果是

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前后他们是死是活。

Code


    public void Register(Object o)
    {
        m_lstObjectRecord.Add(
new ObjectRecord(o));
    }

   
/// 
   
/// 显示注册到跟踪器的对象的生死:)
   
/// 
    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重新赋给一个普通的对象,会发生什么事情呢?下一个例子给出了解释:

Code

输出结果如下:

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可以使对象复生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值