【问题标题】:Explicit nulling显式归零
【发布时间】:2010-10-15 09:33:33
【问题描述】:

在 java 中什么情况下显式置空有用。它是否以任何方式通过使对象无法访问或其他方式来帮助垃圾收集器?这被认为是一种好的做法吗?

【问题讨论】:

    标签: java


    【解决方案1】:

    在 Java 中,如果您有一个运行时间很长的方法,它可以提供帮助,并且对对象的唯一引用是通过局部变量。当您不再需要该局部变量时将其设置为 null(但当该方法将继续运行很长时间时)可以帮助 GC。 (在 C# 中,这很少有用,因为 GC 会考虑“最后可能的使用”。这种优化可能会在一段时间内用于 Java - 我不知道。)

    同样,如果您有一个引用对象的成员字段并且您不再需要它,您可以通过将该字段设置为 null 来帮助 GC。

    然而,根据我的经验,做这些事情很少实际上有用,而且它使代码更加混乱。很少有方法真的会运行很长时间,而将变量设置为 null 确实与您希望方法实现的目标无关。当您不需要时这样做不是一个好习惯,如果您确实需要,您应该首先查看重构是否可以改善您的设计。 (可能是你的方法或类型做的太多了。)

    请注意,将变量设置为 null 完全是被动的 - 它不会通知垃圾收集器该对象可以被收集,它只是避免垃圾收集器将该引用视为下次保持对象活动的原因( GC) 运行。

    【讨论】:

      【解决方案2】:

      通常不需要它(当然这可能取决于 VM 实现)。但是,如果你有这样的事情:

      private static final Map<String, String> foo;
      

      然后在地图中有您不再需要的项目将不符合垃圾收集条件,因此您需要明确删除它们。有很多这样的情况(事件侦听器是另一个可能发生这种情况的领域)。

      但是做这样的事情:

      void foo()
      {
          Object o;
      
          // use o
      
          o = null; // don't bother doing this, it isn't going to help
      }
      

      编辑(忘了提这个):

      如果你使用它,你会发现你声明的 90-95% 的变量都可以是 final 的。 final 变量不能改变它指向的东西(或者它的原始值)。在大多数情况下,如果一个变量是 final 的,那么在方法执行时它接收到不同的值是一个错误(错误)。

      如果你希望能够在使用后将变量设置为null,则它不能是最终的,这意味着你有更大的机会在代码中创建错误。

      【讨论】:

      • 关于“最终”部分的要点。 +1。但是,我知道一些程序员似乎考虑添加 final 来“整理”他们的代码!看到这个答案:stackoverflow.com/questions/132777/…
      • 很多程序员都是这么想的。这几乎是一个宗教论点。
      • 我喜欢任何让编写错误而不是代码更难的东西:-)
      • 喜欢可读性?正如我所说,这归结为宗教争论。没有真正的对错,只是个人意见和喜好。我不希望别人改变他们的看法,我只是表达我的看法。
      • 我不认为 final 的可读性是一个问题,但正如你基本上所说的,每个人都有自己的问题。 (也是:-) 通常意味着我在开玩笑)
      【解决方案3】:

      我发现它很有用的一个特殊情况是当您有一个非常大的对象并想用另一个大对象替换它时。比如看下面的代码:

      BigObject bigObject = new BigObject();
      // ...
      bigObject = new BigObject(); // line 3
      

      如果 BigObject 的实例太大以至于堆中只能有一个这样的实例,第 3 行将失败并出现 OutOfMemoryError,因为在第 3 行中的赋值指令完成之前无法释放第一个实例,这显然是在第二个实例已准备就绪。

      现在,如果您在第 3 行之前将 bigObject 设置为 null:

      bigObject = null;
      bigObject = new BigObject(); // line 3
      

      在构造第二个实例的过程中,当 JVM 用完堆时,可以释放第一个实例。

      【讨论】:

      • 一个非常罕见的案例,但确实如此。
      • 虽然如果发生这种情况,我怀疑你在设计方面会有更大的问题。
      【解决方案4】:

      来自“Effective Java”:用它来消除过时的对象引用。否则会导致内存泄漏,很难调试。

      public Object pop(){
          if(size == 0)
              throw new EmptyStatckException();
          Object result = elements[--size];
          elements[size] = null; //Eliminate Object reference
          return result;
      }
      

      【讨论】:

      • 你确定要elements[size] == null AFTER 你已经减小了大小?
      • @AndrewLazarus 对我来说看起来很正确。想象一个大小为 1 的堆栈。它在elements[0] 存储了一个元素。元素作为结果缓存,数组中的位置无效,导致堆栈大小为 0。
      【解决方案5】:

      如果您在方法块关闭时将一个即将超出范围的对象清空,那么在垃圾收集方面没有任何好处。遇到不理解这一点的人,他们非常努力地将很多东西设置为不必要的空,这并不罕见。

      【讨论】:

        【解决方案6】:

        另请参阅 J2SE 中的 WeakReference

        【讨论】:

          【解决方案7】:

          在满足以下所有条件的极少数情况下,显式置空有助于 GC:

          • 变量是对对象的唯一(非弱)引用
          • 您可以保证不再需要该对象
          • 变量将停留在范围内很长一段时间(例如,它是长期对象实例中的一个字段)
          • 编译器无法证明该对象不再被使用,但您可以通过您对代码的出色逻辑分析来保证这一点:-)

          实际上,这在好的代码中是非常罕见的:如果不再需要该对象,您通常应该在更窄的范围内声明它。例如,如果您只需要在单个方法调用期间使用该对象,则它应该是一个局部变量,而不是封闭对象中的一个字段。

          显式归零真正有用的一种情况:如果使用 null 来指示特定状态,那么设置为 null 值有时是必要且有用的。 Null 本身就是一个有用的值,原因如下:

          • 空值检查非常快,因此检查空值的条件代码通常比许多替代方法(例如调用 object.equals())更有效
          • 如果您尝试取消引用它,您会立即收到 NullPointerException。这很有用,因为它是一种很好的 Fail Fast 编码风格,可以帮助您捕获逻辑错误。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-14
            • 2011-08-30
            • 1970-01-01
            • 1970-01-01
            • 2016-12-19
            • 2020-06-14
            • 2021-09-18
            • 1970-01-01
            相关资源
            最近更新 更多