在Dispose中使用/不使用SuppressFinalize

假设如下:

  • 一个班级只管理成员。
  • 一些成员实施IDisposable
  • 该类是sealed - 一个类不能派生并添加非托管资源。
  • 该对象在using语句中using - 即完成时调用Dispose()
  • 这个类有3种可能的IDisposable实现:

  • IDisposable成员上调用Dispose()最小Dispose方法 - 无终结器
  • Finalizer的标准IDisposable实现但缺少 Dispose()通常的GC.SuppressFinalize(this)调用。
  • Finalizer的完整标准IDisposable实现(以及Dispose() GC.SuppressFinalize(this)调用)。
  • 以下声明是否正确? 我对此有正确的理解吗?

  • 情况A比B和C的开销稍小,因为对象没有终结器,所以它不会进入GC终止队列 - 因为GC可以在集合中尽早清理该对象 - 没有开销。
  • 情况B.对象有一个终结器,所以最终会在GCs终结器队列中结束,并且终结器会得到调用(因为它没有被抑制) - 终结器调用处理,因为它已经被调用,所以不做任何事情。 这导致了终结器队列中的对象的小开销以及终结器调用的非常小的开销。
  • 情况C.对象具有终结器,因此仍然会在GC终结器队列中结束。 因为已经调用了Dispose和SuppressFinalize ,所以终结器将不会运行。 这种情况仍然导致进入终结器队列的对象的小开销,但终结器并不实际运行。
  • 关键的一点是,想到“我通过调用SuppressFinalize避免了终结器开销” - 但我认为( 并且想澄清 )这是不正确的。 在终结器队列中的对象的开销仍然产生 - 你所要避免的是实际的终结器调用 - 在一般情况下,这只是“我已经处置无用”了。

    注意:这里的“完全标准的IDisposable实现”是指设计用于涵盖非托管资源和管理资源的标准实现(注意这里我们只有托管对象成员)。

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    private bool _disposed;
    protected virtual void Dispose(bool disposing) {
    if (_disposed)
        return;
        if (disposing) {
            // dispose managed members...
        }
        _disposed = true;
    }
    
    ~AXCProcessingInputs() {
        Dispose(false);
    }
    

    不同版本的.NET GC可能会做不同的事情,但根据我的理解,任何具有Finalize方法的对象都将添加到“终结器队列”(已放弃请求通知的对象列表)中,并将保留在该队列中只要它存在。 注销和重注册以完成定义的方法(其中,IMHO应该是Object成员)设置或清除对象头中的一个标志,该标志控制对象是否应移动到“可排队队列”( finalize方法应该如果发现它被放弃,但不会导致对象被添加或从终结器队列中移除。

    每个覆盖Finalize类型的每个实例都会因此对它所参与的每个垃圾收集周期施加一个小但非零的开销,只要它存在。 在放弃它之前调用对象的SuppressFinalize可以防止它被移动到可放入队列中,但不会消除由于它在整个存在期间一直处于可终止队列而导致的开销。

    我会建议没有面向公众的对象应该实施FinalizeFinalize方法有一些合法的用法,但是覆盖它的类应该避免保存对最终化不需要的任何东西的引用(有点令人讨厌的是,如果可终结对象只包含对弱引用的引用,那么弱引用可能会在终结器之前失效即使弱参考的目标仍然存在)。


    您只应在需要清理非托管资源的对象上包含终结器。 由于您只有托管成员,因此您不需要终结器 - 终结器将在成员自身上运行(如果它们具有终结器),并且GC.SuppressFinalize()未被调用。

    终结器的目标是每当GC感觉到它时清理非托管资源及其托管包装,而Dispose模式的目标是在特定时刻清理任何类型的资源。

    没有人会认为“我通过调用SuppressFinalize避免了终结器的开销” - 相反,他们应该认为“我已经清理了我的非托管资源,没有必要运行终结器”。

    关于编号问题:

  • 是的,进行终结器运行会在收集时产生一些开销
  • 是的,应该忽略已经处理的对象上的Dispose()
  • 当创建一个实例时,这些类会被添加到最终化队列中,但是当GC试图收集时,这些类将不会被添加到可扩展队列中 - 将对象排成队列只会稍后忽略它。 另请参阅此链接。
  • 链接地址: http://www.djcxy.com/p/54043.html

    上一篇: with/without SuppressFinalize in Dispose

    下一篇: mark methods as safe