Почему GC не может понять это? - PullRequest
6 голосов
/ 23 июля 2011

У меня простой вопрос, почему 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);
    }
}

Ответы [ 5 ]

7 голосов
/ 23 июля 2011

Не зависит от ГХ, используйте шаблон 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

4 голосов
/ 23 июля 2011

Почему вы ожидаете, что активный таймер может быть собран в первую очередь? Я ожидаю, что он действует как корень GC. В противном случае таймер перестанет работать только потому, что у вас больше нет ссылки.

4 голосов
/ 23 июля 2011

Вы не утилизируете таймер после использования.Вот что задерживает его коллекцию.

Если ваш класс содержит объекты, которые реализуют 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.

2 голосов
/ 23 июля 2011

Когда вы запускаете таймер timer.Start(), новый фон (ы) будет запускаться в фоновом режиме. Когда вы звоните timer = null;, вы не останавливаете поток (ы), который использовал таймер.Сборщик мусора не будет уничтожать или прерывать потоки, работающие независимо от исходного объекта, который создает эти потоки.

1 голос
/ 23 июля 2011

Оказывается, что этот параметр состояния (и делегат TimerCallback) оказывают интересное влияние на сборку мусора: если ни один из них не ссылается на объект System.Threading.Timer, он может быть собран сборщиком мусора, вызывая его остановку.Это связано с тем, что и делегат TimerCallback, и параметр состояния заключены в GCHandle.Если ни один из них не ссылается на объект таймера, он может иметь право на GC, освобождая GCHandle от его финализатора.

Подробнее см. этот поток.

...