我的问题很简单,为什么GC不能弄清楚main
中的timer
对象应该与TestTimer
中的timer
和相关的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);
}
}
不要依赖于GC,使用Dispose模式来正确地处置TestTimer
(然后应该处置Timer
)。
然而,发生的情况是定时器通过获取自身的GC句柄来保持自身存活。阅读这篇博文:
http://nitoprograms.blogspot.com/2011/07/systemthreadingtimer-constructor-and.html?utm_source=feedburner& utm_medium = feed& utm_campaign =提要% 3 + blogspot % 2 folztt + % 28 nito +编程% 29日
为什么您首先期望可以收集活动计时器?我的期望是它充当GC根。否则计时器会停止工作,因为你不再有一个引用。
您没有在使用后处理计时器。这就是延迟其收集的原因。
如果你的类包含实现IDisposable
的对象(就像Timer
一样),它也应该实现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
。
当你启动定时器timer.Start()
时,一个新线程将在后台启动,当你调用timer = null;
时,你并没有停止计时器使用的线程。无论创建这些线程的原始对象是什么,垃圾收集器都不会杀死或中止正在运行的线程。
事实证明,这个状态参数(和TimerCallback委托)对垃圾收集有一个有趣的影响:如果它们都没有引用System.Threading.Timer对象,它可能被垃圾收集,导致它停止。这是因为TimerCallback委托和状态参数都被包装到一个GCHandle中。如果它们都没有引用计时器对象,则它可能符合GC的条件,从而将GCHandle从其终结器中释放出来。