【问题标题】:Does setting an object to null dispose the object deterministically?将对象设置为 null 是否会确定地处置对象?
【发布时间】:2013-12-06 09:57:45
【问题描述】:

我有一个奇怪的问题,即使有围绕同一问题的线程,我也找不到明确的答案。

问题:如果我将一个对象设置为 null,这会导致 dispose 方法(已实现)被确定性地调用吗?例如在下面的代码中,通过将pricingEnvironment 对象设置为null,Dispose 会被立即调用吗?我知道如果不调用 Dispose,终结器将在某个时候为定价环境对象启动。

代码:

public interface IPricingService
    {
        double GetPrice(string instrument);
    }

    public interface IPricingEnvironment:IDisposable
    {
        void Initialize();
    }

    public class PricingEnvironment : IPricingEnvironment
    {
        public void Dispose()
        {
            DisposeObject();
        }

        public void Initialize()
        {
            //initialize something leaky
        }

        private void DisposeObject()
        {
            //release some leaky unmanaged resource
        }

        ~PricingEnvironment()
        {
            DisposeObject();
        }
    }

    public class PricingService:IPricingService, IDisposable
    {
        private IPricingEnvironment pricingEnvironment;
        public PricingService()
        {
            pricingEnvironment = new PricingEnvironment();
        }
        public double GetPrice(string instrument)
        {
            pricingEnvironment.Initialize();
            return 1d;
        }

        public void Dispose()
        {
            //Will this dispose the leaky resource used by pricing environment deterministically?
            pricingEnvironment = null;
        }
    }

谢谢, -迈克

【问题讨论】:

  • 作为旁注,我想说,如果你正在处理相对较小的对象,你可以让垃圾收集器独自完成他的工作。我不知道这是否是一个好习惯,但我几乎只在文件或数据库连接上使用dispose()...
  • 不是您的问题的真正副本,而是设置 Null 与 Dispose 的一个非常好的答案 - stackoverflow.com/questions/574019/…
  • @Bartdude:良好的实践表明,每当一个类实现 IDisposable 时,您都应该调用 Dispose 方法
  • @LuisFilipe > 感谢您的见解 :-) 从现在开始,我将把它作为我的编码规则!

标签: c#


【解决方案1】:

.NET 不能保证终结器会被调用。垃圾收集器可能根本不调用它(例如,因为垃圾收集器根本不需要释放内存),并且在一个终结器抛出异常的情况下,其他终结器将不会执行(参见MSDN)。如果你在对象上调用SuppressFinalizer,你甚至可以禁止终结器。

话虽如此,当然也不能保证立即调用终结器(它可能会在很久以后调用或根本不调用)。

您应该显式调用Dispose 或使用using 语句,以便您的对象得到正确处理。作为安全网,您仍然可以从终结器调用Dispose。事实上,MSDN 中的示例也证明了这是一个最佳实践。

Raymond Chen 的帖子很好地阅读了该主题:

Everybody thinks about garbage collection the wrong way

【讨论】:

    【解决方案2】:

    不,不会的。

    这样做:

        public void Dispose()
        {
            // check if object has IDisposble implemented
            IDisposable disposePricing = pricingEnvironment as IDisposable;
            if (disposePricing!=null)
            {
                disposePricing.Dispose();
            }
        }
    

    阅读this CLR Inside Out article from 2007

    【讨论】:

      【解决方案3】:

      虽然计算机在定义上是确定性的,但当分配的内存被释放时,完全取决于语言实现的垃圾收集器。将其设置为 null 会导致对分配的内存的引用计数减少,但在任何情况下都会发生这种情况,当到达范围的末尾时。

      【讨论】:

        【解决方案4】:

        没有*方法可以确定垃圾收集器何时收集您的对象并调用其终结器(并且按照代码中的逻辑,Dispose 方法)。 这是一个很好的例子来说明垃圾收集是多么不可预测:http://ericlippert.com/2013/06/10/construction-destruction/

        即使 GC 启动并收集第一代对象,您的 PricingEnvironment 实例也会在第一次收集中幸存下来并被放入终结队列中。

        您应该通过显式调用Dispose 或使用using 块来确定地处置您的对象。

        *您实际上可以使用GC.Collect() 强制它,但您不应该这样做。

        【讨论】:

          【解决方案5】:

          我猜你把处理和垃圾收集混淆了。

          Dispose 释放非托管资源(数据库连接或文件句柄)

          GC 通过不再使用的对象释放占用的内存

          Disposing 是确定性的,因为它在您调用 Dispose 时运行(无论是在 using 块中显式还是隐式)

          垃圾收集本质上是不确定的。它在合适的时候运行。只有这样它可能释放内存引用。 例如,如果 GC 运行的生成级别与对象当前所在的生成级别不同,则不会被收集。

          您可以通过调用 GC.Collect() 显式运行 GC,但问问自己是否应该再次猜测 GC。

          【讨论】:

            猜你喜欢
            • 2013-11-15
            • 2010-10-09
            • 1970-01-01
            • 1970-01-01
            • 2016-04-27
            • 2019-07-15
            • 1970-01-01
            • 2016-07-28
            • 2011-03-03
            相关资源
            最近更新 更多