为什么GC不能解决这个问题?

时间:2011-07-23 10:36:06

标签: c# event-handling garbage-collection

我的问题很简单,为什么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}}

5 个答案:

答案 0 :(得分:7)

不依赖于GC,使用Dispose模式正确处理TestTimer(然后应该处置Timer)。

然而,发生的事情是计时器通过自己获取GC句柄来保持活跃。阅读此博客文章:

http://nitoprograms.blogspot.com/2011/07/systemthreadingtimer-constructor-and.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FOlZtT+%28Nito+Programming%29

答案 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主题。