代码之家  ›  专栏  ›  技术社区  ›  ben ben

Java和C的终结器#

  •  1
  • ben ben  · 技术社区  · 10 年前

    Java语言

    在Java中,有一个成语叫做“ 定案监护人 这防止子类重写超类终结器,但忘记调用它 有效Java项目7 :

    // Finalizer Guardian idiom
    public class Foo {
      // Sole purpose of this object is to finalize outer Foo object
      private final Object finalizerGuardian = new Object() {
        @Override protected void finalize() throws Throwable {
          ... // Finalize outer Foo object
        }
      };
      ... // Remainder omitted
    }
    

    使用这种技术,即使具有终结器的子类没有调用超类的终结器,私有对象也会运行超类的结束代码。


    C类#

    然而,在 Nutshell中的C# ,节“ 从终结器调用Dispose “,有一个这样的例子:

    class Test : IDisposable {
      public void Dispose() // NOT virtual {
        Dispose (true);
        GC.SuppressFinalize (this); // Prevent finalizer from running.
      }
    
      protected virtual void Dispose (bool disposing) {
        if (disposing) {
          // Call Dispose() on other objects owned by this instance.
          // You can reference other finalizable objects here.
          // ...
        }
        // Release unmanaged resources owned by (just) this object.
        // ...
      }
    
      ˜Test() {
        Dispose (false);
      }
    } 
    

    提交人还指出:

    这个 disposing flag表示从 Dispose 方法,而不是在终结器的最后手段模式下。这个想法 那是用 处置,处置 设置为 false ,此方法应 通常,不使用终结器引用其他对象(因为这样 对象本身可能已经完成 不可预测状态)


    问题

    但是,当我们回顾Java的终结器守护者习惯用法时,内部私有守护者对象实际上引用/终结了外部对象,而外部对象本身可能具有终结器。 这违反了 Nutshell中的C# 规定的。

    我很好奇为什么“在终结器中引用其他可终结对象”在Java中是可能的,而在C#中是不可能的。 谢谢你的回答。

    3 回复  |  直到 10 年前
        1
  •  3
  •   Alexei Levenkov    10 年前

    首先,在C#中,不能“忘记”在派生类中调用基类终结器,因为没有语法来重写基类终结器-它将始终被调用(类似于构造函数)。

    事实上,一个人可以超越 Dispose 并且忘记调用基类的版本-在这种情况下,将跳过在基类中完成的实现。另一方面,如果派生类忘记处理父类,那么在C#中正确实现finalizable类不会导致致命问题——真正管理本机资源(如OS句柄)的类应该被密封,从而免受问题的影响(因为在这种情况下不能覆盖任何方法)。

    关于最后确定订单的后半部分:

    我不知道Java终结是如何或者是否保证对象以一致的顺序终结的,所以在所有终结器完成之前,所有引用都是有效的。。。

    在.Net/C#终结器顺序未定义-这意味着在确定需要终结的对象(由于缺少外部引用)之后,该集合中的对象将在没有任何特定顺序的情况下被调用。因此,如果集合中的对象彼此引用,而不是调用最后一个对象的终结器时,所有其他对象都已终结。

        2
  •  1
  •   fejesjoco    10 年前

    我觉得没那么简单。我认为您可以引用.net和java中的其他对象,而其他对象处于不可预测状态也存在同样的问题。人们需要研究两者的内部工作机制才能完全理解它们,但我对java的了解并不深入,我只认为它们非常相似。顺便说一句,这个终结守护者在我看来很可疑。

        3
  •  1
  •   supercat    10 年前

    您没有显示终结器守护程序的代码,但我建议您应该避免让可终结对象保留对原始对象的引用。相反,原始文件中需要清理的那些方面应该封装在可终结对象中(可能使用 AtomicReference AtomicInteger ). 例如,封装OS文件句柄的对象可以将句柄本身封装在私有可终结对象中 原子整数 。如果外部对象被放弃,则可以清理手柄,而无需任何东西访问外部对象。如果外部对象被要求关闭文件,它可以将请求转发给内部对象,然后内部对象可以读取并清除 原子整数 存储句柄的位置(因此确保文件只能关闭一次)。