首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Dispose中使用带有/不带SuppressFinalize的终结器的开销

在Dispose中使用带有/不带SuppressFinalize的终结器的开销
EN

Stack Overflow用户
提问于 2015-03-19 09:12:08
回答 3查看 967关注 0票数 3

假设如下:

  • 类只管理成员。
  • 一些成员实现IDisposable
  • 类是sealed --类不能从非托管资源派生和添加非托管资源。
  • 对象在using语句中使用--即在完成时调用Dispose()

这个类有3种可能的IDisposable实现:

  1. 调用Dispose成员上的Dispose()的最小IDisposable方法--无终结器
  2. 使用终结器的标准IDisposable实现,但缺少,这是Dispose()中通常的GC.SuppressFinalize(this)调用。
  3. 带有终结器的完整标准IDisposable实现(以及在Dispose()中使用GC.SuppressFinalize(this)调用)。

下列语句正确吗?我明白了吗?

  1. 案例A的开销略小于B.C.,因为对象没有终结器,因此它不会进入GC的终结队列--因为GC可以在集合的早期清除该对象--没有开销。
  2. 案例B.对象有一个终结器,因此最终将在GCs终结器队列中结束,终结器将得到调用(因为它没有被抑制)--终结器调用dispose,因为它已经被调用了,所以什么也不做。这会导致在终结器队列中的对象的小开销和终结器调用的非常小的开销。
  3. 情况C.对象有终结器,因此仍将在GCs终结器队列中结束。因为已经调用了dispose以及因此调用了SuppressFinalize,所以终结器不会运行。这种情况仍然会导致对象在终结器队列中运行的开销很小,但是终结器实际上不会运行。

这里的关键点是,人们很容易认为“我通过调用SuppressFinalize避免了终结器开销”--但我认为(并想澄清)这是不正确的。在终结器队列中的对象的开销仍然会发生--您所避免的只是实际的终结器调用--在常见情况下,这只是“我已经被处理了,什么都不做”。

注意:这里所说的“完全标准IDisposable实现”指的是为涵盖非托管和托管资源情况而设计的标准实现(请注意,这里我们只有托管对象成员)。

代码语言:javascript
复制
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);
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-03-19 16:40:44

不同版本的.NET GC可能会做不同的事情,但据我所知,任何具有Finalize方法的对象都将被添加到“终结器队列”(如果放弃请求通知的对象列表),并且只要它存在就会留在队列中。取消注册和重新注册以便最后完成的方法( IMHO应该是Object的受保护成员)在对象标头中设置或清除一个标志,该标志控制对象是否应该移动到“可自由队列”( finalize方法应该在实际情况下立即运行的对象列表),但不会导致对象被添加或从终结器队列中删除。

因此,覆盖Finalize的每一种类型的每个实例都会对它所参与的每个垃圾回收周期(只要它存在)施加一小部分但非零的开销。在放弃对象之前在对象上调用SuppressFinalize将防止它被移动到可自由的队列,但不会消除由于它在整个存在过程中一直处于可终结队列而造成的开销。

我建议任何面向公共的对象都不应该实现FinalizeFinalize方法有一些合法的用法,但是覆盖它的类应该避免保存对任何不需要终结的引用(有些烦人的是,如果可终结的对象持有对弱引用的唯一引用,弱引用可能在终结器运行之前失效,即使弱引用的目标仍然活着)。

票数 4
EN

Stack Overflow用户

发布于 2015-03-19 10:53:37

您应该只在需要清理非托管资源的对象上包含终结器。因为您只有托管成员,所以不需要终结器--如果成员本身有终结器,并且不为它们调用GC.SuppressFinalize(),则终结器将在它们上运行。

终结器的目标是在GC喜欢的时候清理非托管资源及其托管包装器,而Dispose模式的目标是在特定时刻清理任何类型的资源。

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

关于编号问题:

  1. 是的,在收集终结器时,运行终结器会导致一些开销。
  2. 是的,在已经释放的对象Dispose()上调用应被忽视
  3. 这些类是在创建实例时添加到终结队列中的,而不是在GC跟踪收集对象时添加到可自由访问的队列中--只在稍后对对象进行排队以忽略它是没有意义的。另见此链接
票数 1
EN

Stack Overflow用户

发布于 2019-04-25 14:53:44

有时,我会使用终结器进行调试,以检查是否在某个地方丢失了一些dispose。如果有人感兴趣,我对我的系统进行了快速测试,以检查性能影响(Windows10,.Net 4.7.1,IntelCorei5-8250U)。

添加终结器并抑制它的成本大约为每对象60 ns /,而添加它和忘记调用dispose的成本大约为每对象 800 ns。性能影响与调试/发布版本以及/不附加调试器非常一致,这可能是因为垃圾收集器在这两个版本中都是相同的。

添加终结器并抑制它的性能影响是最小的,除非您正在构造大量的这些对象,这通常不是这样的。甚至微软自己的Task也使用终结器(几乎总是被禁止的),而且这个类非常轻量级和性能良好。所以他们显然同意。

然而,让终结器运行可能会变得非常糟糕。考虑到我的测试用例使用了一个没有引用对象的琐碎类,而且它已经慢了一个数量级。拥有大量的引用对象应该花费更多的时间,因为所有这些对象都需要在额外的世代中保持存活。这也会导致大量复制在垃圾收集的压缩阶段发生。

测试的源代码:

代码语言:javascript
复制
using System;
using System.Diagnostics;

namespace ConsoleExperiments
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            GenerateGarbageNondisposable();
            GenerateGarbage();
            GenerateGarbageWithFinalizers();
            GenerateGarbageFinalizing();

            var sw = new Stopwatch();

            const int garbageCount = 100_000_000;

            for (var repeats = 0; repeats < 4; ++repeats)
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                sw.Restart();
                for (var i = 0; i < garbageCount; ++i)
                    GenerateGarbageNondisposable();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Console.WriteLine("Non-disposable: " + sw.ElapsedMilliseconds.ToString());

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                sw.Restart();
                for (var i = 0; i < garbageCount; ++i)
                    GenerateGarbage();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Console.WriteLine("Without finalizers: " + sw.ElapsedMilliseconds.ToString());

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                sw.Restart();
                for (var i = 0; i < garbageCount; ++i)
                    GenerateGarbageWithFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Console.WriteLine("Suppressed: " + sw.ElapsedMilliseconds.ToString());

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                sw.Restart();
                for (var i = 0; i < garbageCount; ++i)
                    GenerateGarbageFinalizing();
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Console.WriteLine("Finalizing: " + sw.ElapsedMilliseconds.ToString());

                Console.WriteLine();
            }

            Console.ReadLine();
        }



        private static void GenerateGarbageNondisposable()
        {
            var bla = new NondisposableClass();
        }

        private static void GenerateGarbage()
        {
            var bla = new UnfinalizedClass();
            bla.Dispose();
        }

        private static void GenerateGarbageWithFinalizers()
        {
            var bla = new FinalizedClass();
            bla.Dispose();
        }

        private static void GenerateGarbageFinalizing()
        {
            var bla = new FinalizedClass();
        }



        private class NondisposableClass
        {
            private bool disposedValue = false;
        }

        private class UnfinalizedClass : IDisposable
        {
            private bool disposedValue = false;

            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                    }

                    disposedValue = true;
                }
            }

            public void Dispose()
            {
                Dispose(true);
            }
        }

        private class FinalizedClass : IDisposable
        {
            private bool disposedValue = false;

            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                    }

                    disposedValue = true;
                }
            }

            ~FinalizedClass()
            {
                Dispose(false);
            }

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29140628

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档