困惑。 CA1063错了? GC.SuppressFinalize(本)

时间:2012-08-03 09:37:32

标签: c# .net garbage-collection

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

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警告,告诉我应该在 Dispose()方法中调用 GC.SuppressFinalize()方法> MyDataClass 实施。

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

“嘿GC,不要担心这个对象,因为我已经完成了所有的清洁工作!”

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

3 个答案:

答案 0 :(得分:5)

方法GC.SuppressFinalize()表示VM不运行终结器。这是C#中看起来很滑稽的方法:

~MyDataClass()

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

答案 1 :(得分:4)

尽管我已经接受了答案,但我还是觉得你仍然感到困惑。让我解释一下:

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

可达性

只有从任何GC root 到对象的任何引用链时,才能访问对象A。根的示例是堆栈和任何静态字段。因此,要确定是否可以访问A,所有GC必须做的是在堆栈上找到引用具有引用引用对象的引用的引用...引用对象{{ 1}}。如果找不到这样的链,则无法访问对象A 1

因此,一旦GC确定A无法访问,它就会想要从内存中删除它。 但是,在执行此操作之前,GC会检查A是否具有必须运行的终结器(A)。如果没有,它会从内存中删除~A并使用它完成GC。

定稿

但是,如果A具有必须运行的终结器,则在终结器完成之前,它无法从内存中删除该对象。因此,它将A的引用添加到终结器队列,并且不会从内存中删除该对象(尚未)。现在,垃圾收集器已完成A。但是,当GC再次运行时,它将再次尝试确定是否可以访问A。幸运的是,终结器队列也是垃圾收集器的根之一,因此它确定终结器队列中有一个引用到A,因此A可以访问,并且不会再从内存中删除。

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

清理

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


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

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

可以找到有关垃圾收集器的更深入信息in this MSDN article


1 可达性是在处理它们之后设置对GC.SuppressFinalize()的引用的常见原因。这使得引用的对象很可能无法访问,因此被删除的候选对象。

2 复活 null可能(但肯定推荐)使用终结器从另一个可到达的对象或根(例如静态字段)添加对A的引用。这使A再次可以访问,垃圾收集器不会删除它。但是,它的终结器不会再次运行,因为它已被调用过一次。

答案 2 :(得分:2)

  

如果我要添加GC.SuppressFinalize(),我将摆脱CA1063   但这会导致GC无法清理我的物体。

不,您的对象仍将被收集。

  

“嘿GC,不要担心这个对象,因为我已经完成了所有的清洁工作!”

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

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