【问题标题】:Measuring the time to create and destroy a simple object测量创建和销毁一个简单对象的时间
【发布时间】:2010-04-03 14:06:43
【问题描述】:

来自 Effective Java 2nd Edition 第 7 条:避免终结器

“哦,还有一件事:使用终结器会严重影响性能。在我的机器上,创建和销毁一个简单对象的时间约为 5.6 ns。添加终结器会将时间增加到 2,400 ns。在换句话说,使用终结器创建和销毁对象的速度大约慢了 430 倍。”

如何衡量创建和销毁对象的时间? 你只是这样做:

long start = System.nanoTime();
SimpleObject simpleObj = new SimpleObject();
simpleObj.finalize();
long end = System.nanoTime();
long time = end - start;

【问题讨论】:

    标签: java time object measurement finalizer


    【解决方案1】:

    这仅测量执行 finalize 方法的时间。最终确定的绝大部分成本将用于 GC 必须执行的特殊处理。

    【讨论】:

    • 好的,如果我在上面的 simpleObj.finalize() 之后包含 Sytem.gc() 会怎样?
    • 而花费的周期使 GC 更加复杂。您不能对此进行微基准测试。
    • @Tom 那么Joshua Bloch是如何测量书中记载的时间的呢?
    • @portoalet 我没有这本书。然而,Josh 曾在 Sun 和 Google 从事 Java 方面的工作。我猜有一些大型系统可以在那里进行基准测试。 (NIO 在 IIRC 1.4.1 中删除了终结器,因为它们会导致性能问题。)
    • 嗯好吧,我想我只需要问一下 Joshua 他是如何测量的...问题是我找不到他的电子邮件地址?
    【解决方案2】:

    与其像 Tom Hawtin 上面所说的那样徒劳地尝试对此进行微基准测试,最好的方法是检查执行几十万个创建和销毁周期的时间,然后除以周期数。

    在很多情况下,这种方法是最好的,因为您编写的代码不是唯一涉及的代码 - 您依赖于系统调用或外部资源。

    我认为这就是 Joshua Bloch 在这种情况下所做的,但我的副本在楼上,我感觉很懒。

    【讨论】:

    • 这本书并没有说明 Josh 如何进行微基准测试。所以基本上你是说把我上面的代码放在一个 for 循环中,然后平均时间?
    • 是的。尽量排除任何其他设置成本、类加载等(例如,在开始计时之前创建和清空一些实例)。
    • 你如何测试对象的实际破坏?据我所知,JVM 规范甚至没有推动实现来完成所有对象,或者我错了
    • @jaime 比较“有终结器”和“没有终结器”的方式更难做到这一点(在单个对象上可能是不可能的)。但是通过在很长一段时间内做很多很多的对象,终结器的重要性应该会变得很明显。
    【解决方案3】:

    您可以通过Runtime.gc() 请求运行时执行垃圾收集,但不需要垃圾收集器立即执行它:

    • finalize() 方法不是由您调用,而是由垃圾收集器调用
    • 您可以简单地将simpleObj 设置为null,然后调用垃圾收集来实现更真实的场景

    【讨论】:

      【解决方案4】:

      一种可能的解决方案是依赖java.lang.ref 包,它只支持与垃圾收集器进行有限程度的交互。

      测量结果不可能准确,因为很难知道一个物体的真实结局。我相信 Joshua 一定有另一种方法来测量时间,也许可以去 JVM 本身。

      PhantomReference 最接近对象生命周期结束的引用。换句话说,该对象是幻象可达的

      public class WithoutFinalizationV1 {
          
          public static void main(String[] args) {
              ReferenceQueue<WithoutFinalizationV1> queue = new ReferenceQueue<>();
              long start = System.nanoTime();
                  
              PhantomReference<WithoutFinalizationV1> rf = new PhantomReference<>(
              new WithoutFinalizationV1(), queue);
              System.gc(); //advise JVM to do GC
                  
              Object x = null;
              int waitCount = -1;
              do{
                  x = queue.poll();
                  waitCount++;
              } while(x == null);
                  //only need this time point
              System.out.println("WithoutV1 "+ waitCount + " " + (System.nanoTime() - start));
          }
      }
      

      跑了几次,世界纪录是5394860ns,和5.6ns相差甚远。

      添加后

      @Override
      protected void finalize() throws Throwable {
      }
      

      结果是 5632208ns。

      Here 是我写的相关帖子的摘录。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-02
        • 1970-01-01
        • 1970-01-01
        • 2014-02-07
        • 2023-03-14
        • 2017-06-29
        • 2012-02-06
        相关资源
        最近更新 更多