C#具有运行计时器的对象的垃圾收集

时间:2017-03-30 09:24:16

标签: c# timer garbage-collection

我创建了一个对象,需要在设置的时间间隔内更新它的状态,所以我正在使用Timer。其中许多对象显示在不同的列表中。当重新加载这样的列表(经常发生)时,不再引用该对象,计时器仍在运行。

我知道我可以通过调用dispose来阻止这种情况,但使用此对象的开发人员可能忘记实现对dispose的调用,我认为会导致市长内存泄漏,我是否正确,我可以使用哪些工具来制作确保对象被垃圾收集。

以下是如何使用类的简化版本。

对象

public class Test : IDisposable
{
    protected readonly Timer Timer;
    protected int Count = 0;
    private bool disposed = false;

    public Test()
    {
        Timer = new Timer();
        Timer.Interval = TimeSpan.FromSeconds(1).TotalMilliseconds;

        Timer.Elapsed += Timer_Elapsed;
        Timer.AutoReset = true;
        Timer.Start();
    }

    private void Timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        Count++;
    }

    public void Dispose() // implementing IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free other state (managed objects).
            }
            Timer.Stop();
            Timer.Elapsed -= Timer_Elapsed;
            Timer.Dispose();

            // Free your own state (unmanaged objects). 
            // Set large fields to null.
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    ~Test()
    {
        // Simply call Dispose(false).
        Dispose(false);
    }
}

List类

public partial class MainWindow : Window
{

    public List<Test> TestsCollection = new List<Test>();
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        TestsCollection.Clear();
        TestsCollection.Add(new Test());
        GC.Collect();
    }
}

1 个答案:

答案 0 :(得分:1)

在这种情况下,如果您不Dispose Test Timer_Elapsed,那么似乎确实存在泄漏,并且终结器将无法帮助(至少我的所有测试都表明它是这样的表现)。

如上所述here - 计时器在GCHandle表中内部注册回调,这可以防止在触发回调时回调可能无效的情况。因为你的回调(this)不是静态的 - 这意味着它保留对Test的引用,因此无法收集你的Test对象,因为你的回调有根引用(GCHandle)。现在,您的对象又引用了计时器,因为您将它存储在一个字段中。毕竟,从根到你的public class Test : IDisposable { protected readonly Timer Timer; protected int Count = 0; private bool disposed = false; public Test() { Timer = new Timer(Timer_Elapsed, new WeakReference<Test>(this), 0, 1000); } private static void Timer_Elapsed(object state) { var wr = (WeakReference<Test>) state; Test test; if (wr.TryGetTarget(out test)) { test.Count++; Console.WriteLine(test.Count); } } public void Dispose() // implementing IDisposable { if (!disposed) { Timer.Dispose(); disposed = true; } } } 对象和你的计时器都有引用,它们都不能被垃圾收集。

要解决此问题 - 您需要打破一些强引用。你可以这样做,例如:

System.Timers.Timer

我们首先将System.Threading.Timer替换为Test,并使回调方法成为静态,以便它不再阻止父Test对象的收集。但是,我们需要在回调中以某种方式访问​​WeakReference对象 - 所以我们将Test作为状态传递(我们不能传递对象本身,因为它会创建我们试图避免的相同情况)。在回调中,我们检查Test对象是否仍然存在,如果是,则执行所需操作。

我们也删除了析构函数,因为这里没有必要。当Timer对象被垃圾收集时,也将收集from sklearn.svm import SVC import csv import numpy as np from gensim.models import word2vec from gensim.models.keyedvectors import KeyedVectors model = KeyedVectors.load_word2vec_format('text.model.bin', binary=True) with open('1000.csv', newline='') as csvfile: listwords = csv.reader(csvfile) features = [] labels = [] n = 0 for row in listwords: if n>=199: break try: line = [int(row[2]),np.array(model[row[0]])] features.append([line]) labels.append([row[1]]) n+=1 except KeyError: pass features.append([]) n+=1 clf = SVC() clf = clf.fit(features, labels) vocab_obj = model.vocab['anne'] print (clf.predict([vocab_obj.count,model['anne']])) 并且将调用它自己的析构函数。