【问题标题】:Is it faster (or better) to declare an array inline in Java?在 Java 中声明数组内联更快(或更好)吗?
【发布时间】:2013-12-19 08:56:20
【问题描述】:

考虑以下两个几乎等效的方法调用。注意字节数组的声明和分配方式。

void Method1()
{
    byte [] bytearray = new byte[16];

    /* some code */

}

void Method2()
{
    byte [] bytearray = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    /* some code */    
}

在这两种情况下,假设当 Method1 和 Method2 返回时,“bytearray”是垃圾回收的候选对象,因为操作 bytearray 变量的任何代码都不会在方法本身的末尾持有引用。

通过避免调用“new”,Method2 是否运行得更快(或不同)?或者上面的两个实现是等价的吗?在任何一种情况下,Java 编译器或运行时是否可以进行优化以避免为这个短暂的临时缓冲区命中内存分配器的开销?

【问题讨论】:

    标签: java arrays inline


    【解决方案1】:

    哪种形式最快?取决于 JIT——它们可能是等价的。如果有的话,很少有程序会注意到差异。

    哪种形式最好?几乎无一例外,那种形式可以让您的程序更具可读性。

    但是无论我们是否会注意到,是否有任何实际差异?一起来看看吧!

    class ArrayTest {
    
        public int[] withNew() {
            int[] arr = new int[4];
            return arr;
        }
    
        public int[] withInitializer() {
            int[] arr = {0, 0, 0, 0};
            return arr;
        }
    
    }
    

    我们用javap -c ArrayTest拆解这个:

    Compiled from "ArrayTest.java"
    class ArrayTest {
      ArrayTest();
        Code:
           0: aload_0       
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return        
    
      public int[] withNew();
        Code:
           0: iconst_4      
           1: newarray       int
           3: astore_1      
           4: aload_1       
           5: areturn       
    
      public int[] withInitializer();
        Code:
           0: iconst_4      
           1: newarray       int
           3: dup           
           4: iconst_0      
           5: iconst_0      
           6: iastore       
           7: dup           
           8: iconst_1      
           9: iconst_0      
          10: iastore       
          11: dup           
          12: iconst_2      
          13: iconst_0      
          14: iastore       
          15: dup           
          16: iconst_3      
          17: iconst_0      
          18: iastore       
          19: astore_1      
          20: aload_1       
          21: areturn       
    }
    

    不,在这种情况下它们不一样 - 使用初始化形式会导致插槽被单独设置为 0,这有点毫无意义,因为它们已经被数组分配归零。所以,基本上相当于这样:

    public int[] withNewAndSettingExplicitly() {
        int[] arr = new int[4];
        arr[0] = 0;
        arr[1] = 0;
        arr[2] = 0;
        arr[3] = 0;
        return arr;
    }
    

    虽然编译成另一组字节码,大部分相同但不完全相同:

      public int[] withNewAndSettingExplicitly();
        Code:
           0: iconst_4      
           1: newarray       int
           3: astore_1      
           4: aload_1       
           5: iconst_0      
           6: iconst_0      
           7: iastore       
           8: aload_1       
           9: iconst_1      
          10: iconst_0      
          11: iastore       
          12: aload_1       
          13: iconst_2      
          14: iconst_0      
          15: iastore       
          16: aload_1       
          17: iconst_3      
          18: iconst_0      
          19: iastore       
          20: aload_1       
          21: areturn 
    

    因此,故事的寓意是:如果您希望将所有元素设置为 0,则使用 new int[size] 生成的字节码会更少(可能更快也可能不会更快),但您还必须输入少(恕我直言,这是一个很大的胜利)。如果您想在分配数组时直接设置数组中的值,请使用代码中看起来最好的值,因为无论您选择哪种形式,生成的代码都几乎相同。

    现在,回答您的实际问题:

    通过避免调用“new”,Method2 是否运行得更快(或不同)?

    正如我们所见,new 只是隐藏在初始化语法后面(查找 newarray 操作码)。顺便说一句,JVM 中的分配非常便宜(分代垃圾收集器具有令人愉快的副作用)。

    或者上面的两个实现是等价的吗?

    正如我们所见 - 不完全是,但不太可能有人会注意到差异。

    在任何一种情况下,Java 编译器或运行时是否可以进行优化以避免为这个短暂的临时缓冲区命中内存分配器的开销?

    再一次 - 分配很便宜,所以不用担心。然而,最近的 JVM 有一个名为 escape analysis 的小功能,这可能导致数组被堆栈分配而不是堆分配。

    【讨论】:

      【解决方案2】:

      这两种方式没有区别。语法上只是'Syntactic sugar'

      【讨论】:

        【解决方案3】:

        它们是一样的。

        new 关键字创建一个对象。但在 Java 中,数组是.. 对象。见Chapter 10. Arrays

        在 Java 编程语言中,数组是对象(第 4.3.1 节),是 动态创建,并且可以分配给 Object 类型的变量 (§4.3.2)。可以在数组上调用 Object 类的所有方法。
        ..
        数组由数组创建表达式 (§15.10) 或数组初始化器 (§10.6) 创建。

        【讨论】:

        • 实际上,它们生成相同的字节码,尽管在功能上是相同的。
        【解决方案4】:

        这两种方法是完全等价的。但是,对于较长的数组或要修改数组长度,第二种方法更冗长且不太方便。

        【讨论】:

          【解决方案5】:

          最好使用第一种样式。

          在第二种样式中,我尝试找到一个非 0。它保证更无错误,因此更具可读性。

          这个速记有充分的理由存在!

          【讨论】:

            【解决方案6】:

            两者是相同的...因为两者都将在堆上占用相同的空间...而字节码将是研究它的选项

            【讨论】:

              【解决方案7】:

              两者都是一样的,但我不推荐第二个,因为它很难阅读、编写和调试。想象一下你发现了这个:

              byte [] bytearray = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
              

              你能判断数组的大小是否正确吗?是的,但想象一下:

              byte [] bytearray = new byte[25];
              

              这要容易得多。

              想象一下数组长度为 100 的情况!

              【讨论】:

                猜你喜欢
                • 2010-12-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-09-14
                • 1970-01-01
                • 2012-12-03
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多