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

mdn*_*mdn 7 .net c# 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Mic*_*zyk 5

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

~MyDataClass()

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

  • 是的,`密封'将是更好的解决方案.我希望MS会放弃那个完整的模式(作为_only_官方模式). (3认同)
  • 仅支持托管(IDisposable)资源的模式,没有析构函数. (2认同)

Dan*_*ker 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


Hen*_*man 3

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

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

“嘿GC,不用担心这个物体,因为我已经为你完成了所有的清理工作!”

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

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

  • @spender:很明显,这个类没有析构函数,而且也不应该有。 (2认同)