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

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

主类:

public StartProcess() { Thinker th = new Thinker(); th.DoneThinking += new Thinker.ProcessingFinished(ThinkerFinished); th.StartThinking(); } void ThinkerFinished() { Console.WriteLine("Thinker finished"); } 

思想家class级:

 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被垃圾回收。

显然事实并非如此 。

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

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

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

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

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

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

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

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

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

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

如果我正确地阅读(我可能会离开这里),它可以被收集,因为它目前没有做任何事情。

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

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