7

我有一个简单的 MyDataClass 类,其中包含一个实现 IDisposable 的成员(obj):

public class MyDataClass : IDisposable
{
    private DisposableObject obj;
    private List<string> list;
    private int c;

    public MyDataClass()
    {
        obj = new DisposableObject();
        list = new List<string>();
        c = 114;
    }

    public void Dispose()
    {
        obj.Dispose();
    }
}

public class DisposableObject : IDisposable
{
    public void Dispose()
    {
        // Free resource
        Console.WriteLine("Dispose DisposableObject");
    }
}

当我运行代码分析时,我收到 CA1063 警告,表明我应该在MyDataClass实现的Dispose()方法中调用GC.SuppressFinalize()方法。

我对这个 CA1063 警告感到非常困惑。因为据我所知,我应该调用GC.SuppressFinalize()来指示垃圾收集器:

“嘿 GC,你别管这个对象了,我已经为你做完了所有的清理工作!”

所以请确认我是否错了。如果我将添加GC.SuppressFinalize()我将摆脱 CA1063 但它会导致 GC 不会清理我的对象。所以我会有内存泄漏,因为其他类成员(托管代码)不会被清理。

4

3 回答 3

5

该方法GC.SuppressFinalize()向 VM 指示不要运行终结器。这是 C# 中看起来很有趣的方法:

~MyDataClass()

要删除警告,您需要密封您的课程,或实施完整的IDisposable 模式

于 2012-08-03T09:48:25.530 回答
5

尽管已经接受了答案,但我感觉您仍然感到困惑。让我解释:

垃圾收集器 (GC) 的任务是从内存中删除无法访问的任何对象。

可达性

A仅当从任何 GC到该对象的任何引用链都存在时,该对象才可访问。根的示例是堆栈和任何静态字段。因此,要确定是否A可达,GC 所要做的就是在堆栈上找到一个引用,该引用指向一个对象,该对象的引用指向一个对象……指向 object A。如果找不到这样的链,A则无法访问对象。1

因此,一旦 GC 确定A无法访问它,它就会希望将其从内存中删除。但是,在此之前,GC 会检查是否有一个必须运行A的终结器 ( )。~A如果没有,它会从内存中删除A,GC 会用它完成。

定稿

但是,如果A有一个必须运行的终结器,它不能在终结器完成之前从内存中删除对象。因此,它添加了对A终结器队列的引用,并且还没有从内存中删除对象。现在垃圾收集器已经完成了A。但是,当 GC 再次运行时,它会再次尝试确定是否A可达。幸运的是,终结器队列也是垃圾收集器的根之一,因此它确定终结器队列中有对 的引用A,因此A是可访问的,并且不会再次从内存中删除。

随之而来的是终结器线程,这是一个定期检查终结器队列中是否有任何对象的线程。如果有,它会选择一个并运行它的终结器方法。最终,终结器线程将运行A. 完成此操作后,A将从终结器队列中删除对的引用。

清理

然后,一段时间后,垃圾收集器再次运行,并再次尝试确定是否A可达。由于它现在没有在任何地方引用,甚至从终结器队列中A也没有,因此无法访问。GCA从内存中删除。


你看,通常 GC 可以在它检测到的同一个收集周期中删除无法访问的对象,但是当一个对象有一个需要运行的终结器时,它可能需要多个周期才能收集到对象。因此,CA1063 建议您放入GC.SuppressFinalize()方法Dispose,让 GC 知道对象在从内存中删除之前不需要完成。因此,该对象最终总是从内存中删除2 。

请注意,当您没有终结器时,您不必添加GC.SuppressFinalize(),因此在这方面 CA1063 警告有点多余。

可以在这篇 MSDN 文章中找到有关垃圾收集器的更深入的信息。


1可达性是为什么null在处理它们之后设置引用很常见的原因。这使得引用的对象很可能无法访问,因此成为要删除的候选对象。

2 )有可能(但当然推荐)通过使用终结器添加对另一个可访问对象或根(例如静态字段)的引用来复活。 这使得再次可达,垃圾收集器不会删除它。但是,它的终结器不会再次运行,因为它已经被调用过一次。AAA

于 2012-08-03T14:24:33.860 回答
3

如果我要添加,GC.SuppressFinalize()我将摆脱 CA1063,但这会导致 GC 不会清理我的对象。

不,您的物品仍将被收集。

“嘿 GC,你别管这个对象了,我已经为你做完了所有的清理工作!”

你实际上只是说:不要担心这个对象的终结器(析构函数)。如果有的话。

这就是代码分析出错的地方:你的类确实有一个IDisposable.Dispose()方法,但它没有析构函数。所以警告是没有意义的,过度保护并触发错误的原因。禁用或忽略它。

于 2012-08-03T09:41:35.500 回答