我的问题很简单,为什么GC无法弄清楚timer
中的main
对象应该与timer
TestTimer
内的EventHandler
一起进行垃圾收集并关联console.Writeline
?
为什么我不断获得class Program
{
public static void Main()
{
TestTimer timer = new TestTimer();
timer = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadKey();
}
}
public class TestTimer
{
private Timer timer;
public TestTimer()
{
timer = new Timer(1000);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
private void timer_Elapsed(Object sender, ElapsedEventArgs args)
{
Console.Write("\n" + DateTime.Now);
}
}
输出?
{{1}}
答案 0 :(得分:7)
不依赖于GC,使用Dispose模式正确处理TestTimer
(然后应该处置Timer
)。
然而,发生的事情是计时器通过自己获取GC句柄来保持活跃。阅读此博客文章:
答案 1 :(得分:4)
使用后,您不是处理计时器。这就是推迟其收藏的原因。
如果您的类包含实现IDisposable
的对象(如Timer
所做的那样),那么它也应该implement IDisposable
。
public class TestTimer : IDisposable
{
private Timer timer;
public TestTimer()
{
timer = new Timer(1000);
...
}
#region IDisposable
public void Dispose()
{
Dispose(true);
}
volatile bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (disposing && !disposed)
{
timer.Dispose();
GC.SupressFinalize(this);
disposed = true;
}
}
~TestTimer() { Dispose(false); }
#endregion
}
您的主要方法应如下所示:
public static void Main()
{
using (TestTimer timer = new TestTimer())
{
// do something
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadKey();
}
同样,如果您的TestTimer
应该比单个Main
方法的范围更长,那么创建它并保留其引用的类也应该实现IDisposable
。< / p>
答案 2 :(得分:4)
为什么您希望首先收集活动计时器?我的期望是它充当GC根。否则计时器将因为你没有参考而停止工作。
答案 3 :(得分:2)
启动计时器timer.Start()
时,新线程将从后台启动,
当您调用timer = null;
时,您没有停止计时器使用的线程。无论创建这些线程的原始对象如何,垃圾收集器都不会终止或中止正在运行的线程。
答案 4 :(得分:1)
事实证明,这个状态参数(和TimerCallback委托)对垃圾收集有一个有趣的影响:如果它们都没有引用System.Threading.Timer对象,它可能被垃圾收集,导致它停止。这是因为TimerCallback委托和state参数都被包装到GCHandle中。如果它们都没有引用计时器对象,则它可能符合GC的条件,从而将GCHandle从其终结器中释放出来。
有关详细信息,请参阅this主题。