将.Net Garbage收集一个未被引用的对象,但是有一个正在运行的线程吗?

时间:2009-06-24 04:28:23

标签: c# .net multithreading garbage-collection

我有以下代码(为了便于阅读而减少):

主要类别:

public StartProcess()
{
    Thinker th = new Thinker();
    th.DoneThinking += new Thinker.ProcessingFinished(ThinkerFinished);
    th.StartThinking();
}

void ThinkerFinished()
{
    Console.WriteLine("Thinker finished");
}

思想家类:

public class Thinker
{
    private System.Timers.Timer t;

    public delegate void ProcessingFinished();
    public event ProcessingFinished DoneThinking;

    BackgroundWorker backgroundThread;

    public Thinker() { }

    public StartThinking()
    {
        t = new System.Timers.Timer(5000);    // 5 second timer
        t.AutoReset = false;
        t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
        t.Start();

        // start a background thread to do the thinking
        backgroundThread = new BackgroundWorker();
        backgroundThread.DoWork += new DoWorkEventHandler(BgThread_DoWork);
        backgroundThread.RunWorkerAsync();
    }

    void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        DoneThinking();
    }

    BgThread_DoWork(object sender, DoWorkEventArgs e)
    {
        // work in here should go for much less than 5 seconds
        // it will die if it doesn't

        t.Stop();
        DoneThinking();
    }
}

我最初预计会发生的事情是主类中的事件处理程序会阻止Thinker被垃圾回收。

Apparently this isn't the case

我现在想知道无论这个线程是否“忙”,是否会发生垃圾收集。换句话说,在5秒超时到期之前是否有可能收集垃圾?

换句话说,垃圾收集器是否有可能在完成处理之前收集我的Thinker?

5 个答案:

答案 0 :(得分:8)

不,只要引用一个线程就被认为是活动的,并且任何正在运行的线程都被认为是被引用的(IIRC正在运行的线程将其堆栈注册为GC根,并且该堆栈将引用该线程)。

那说我正在查看你的例子,我不明白你认为线程的产生在哪里?

答案 1 :(得分:5)

不,正在运行的线程的堆栈充当GC的根。只要线程正在运行,该堆栈就会存在,因此只要线程运行它就不会被收集。

这是一个article,它提到了(除其他事项外)GC的用途。为了节省时间,GC根是全局对象,静态对象,所有线程堆栈上的所有引用,以及包含引用的所有CPU寄存器。

答案 2 :(得分:3)

你的问题有点难以回答。和Joel一样,就我所知,你在堆栈上没有任何东西引用你的计时器,这本身就是唯一引用该线程的东西。鉴于此,人们会期望收集Thinker实例。

我对此很好奇,需要对可能发生的事情做出更具体的解释,所以我稍微挖了一下Reflector。事实证明,System.Timers.Timer最终创建了一个System.Threading.Timer,它在内部创建了一个TimerBase实例,一个内部类。 TimerBase派生自CriticalFinalizerObject,它是一种系统类型,可确保在实现类完全最终化并由GC丢弃之前,约束执行区(CER)中的所有代码都将执行。 TimerBase也是IDisposable,它的dispose方法循环和spinwait直到释放锁。此时,我开始遇到外部代码,所以我不确定如何初始化或释放锁。

然而,根据TimerBase类的编写方式,它是从CriticalFinalizerObject派生的事实,以及它的dispose spinwait直到锁被释放的事实,我认为可以安全地说一个没有被任何东西引用的线程在代码执行完毕之前不会最终确定。那就是说...重要的是要注意它很可能会由GC处理......很可能不止一次,因为最终化可以大大延长最终对象的收集过程。对于那些是CriticalFinalizerObjects的人来说,如果积极执行CER确保将完全执行的代码,则最终确定过程可能需要更长的时间。

如果你的思想家需要一段时间来执行,这可能意味着你有完全相反的问题。而不是那些过早收集的对象,它们将进行冗长的定稿,并且它们引用的任何内容都以gen2结束,并且生活了相当长的一段时间,直到GC最终能够完全收集它们。

答案 3 :(得分:2)

如果我正确地阅读(我可以离开这里),可以收集它,因为它 当前正在做任何事情。

如果你的start方法中有局部变量,并且该方法仍处于活动状态,那么这些变量仍然是堆栈中的“范围”并为你的线程提供根。但是你使用的唯一变量是你的私有计时器,因为它以线程本身为根,并且线程在堆栈中没有任何东西,所以没有任何东西可以让它保持活着。

答案 4 :(得分:0)

我同意并且不同意,如果对线程对象的引用丢失,则线程将被终止并且垃圾被收集。在你的情况下,它可能不是这样,因为它不直接使用线程并使用计时器。但是如果你在一个线程中调用了一个方法并且该方法结束时线程引用丢失了那么它将由GC收集