【问题标题】:Java's varargs performanceJava 的可变参数性能
【发布时间】:2011-01-26 10:55:28
【问题描述】:

我来这里是为了检查 Java 的可变参数性能。

我编写以下测试代码:

public class T {

    public static void main(String[] args) {

        int n = 100000000;
        String s1 = new String("");
        String s2 = new String("");
        String s3 = new String("");
        String s4 = new String("");
        String s5 = new String("");

        long t = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            foo();
        }
        System.err.println(System.currentTimeMillis() - t);


        t = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            baz(s1, s2, s3, s4, s5);
        }
        System.err.println(System.currentTimeMillis() - t);

        t = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            bar(s1, s2, s3, s4, s5);
        }
        System.err.println(System.currentTimeMillis() - t);

    }

    static void foo() {
    }

    static void bar(String a1, String a2, String a3, String a4, String a5) {
    }

    static void baz(String... a) {
    }
}

在我的机器上,平均输出是:

78
4696
78

似乎将变量传递给方法是免费的?!好!

但是使用 varags 会慢 60 倍!为什么?

一个解释可能是程序必须在堆上创建数组,而时间被 GC 所花费。但是对于更少的循环,我仍然可以得到输出:

0
62
0

什么是花费这些额外的时间,无论如何编译器有所有信息来解决这个问题,以修复变量调用......

我无意为此优化,但我觉得这很奇怪......

更新

我添加了一个新测试

t = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
    baz(s1);
}
System.err.println(System.currentTimeMillis() - t);

而且这一参数版本仍然慢 30 倍。也许幕后有一个 ArrayList.toArray() ?

因此,请注意代码中不需要的 varags 方法并重构以固定长度。这可能会提高性能。

【问题讨论】:

    标签: java performance


    【解决方案1】:

    参数的静态列表与数组完全不同。当您以这种方式传递它们时,编译器会为引用保留空间并在调用方法时填充它们。

    可变参数是数组的等价物。要调用这样的方法,需要在运行时创建和填充数组。这就是您观察差异的原因。

    String[]String... 是同义词。如果您比较它们,您应该会看到相同的性能。

    【讨论】:

    • 是的,它是一样的,因为可变参数是在有效编译之前转换为数组调用的语法糖。
    • 这基本上是正确的,但我认为您应该澄清不同之处在于可变参数需要 JVM 分配和填充数组。它是在堆上还是在堆栈上都不是问题(虽然是的,当然它在堆上)。
    • 绝对正确,但我仍然感到失望。我想通过一些逃逸分析,热点可以找出数组永远不会保留,并且不需要在堆中分配(和 GCd 关闭)。在这种情况下,即使在编译时也可以进行优化,因为baz(...) 不是“虚拟的”,并且不会遍历数组。
    【解决方案2】:

    同时使用最新的 JRE6 和 JRE7,我得到的结果与你的不同,它们表明 varargs 快 5 倍:

    69
    69
    311
    

    但是,我不会草率下结论,因为这个基准有几个缺陷:函数中没有使用参数;该函数不做任何事情;参数具有相同的值。 JIT 可以轻松优化此代码和内联函数调用。我修改了您的示例以解决上述明显问题,并得到以下结果:

    627
    7470
    7844
    

    结论是:不要犹豫使用可变参数。如果您的函数是微不足道的,那么它的调用将由 JIT 内联,如果不是,那么可变参数的开销可能可以忽略不计。

    【讨论】:

      【解决方案3】:

      有趣的问题!

      这只是一个猜测:在幕后,Var-args 只是数组。创建这个隐式数组并使用 var-args 参数填充它可能需要一些时间;因此性能受到打击。嗯,我猜。

      【讨论】:

        【解决方案4】:

        我重构了一些代码。我现在使用int 而不是String,因为字符串在池中结束并且是不可变的,我不知道这对编译器优化有何影响。

        我还重新创建了这些值,这样它们就不会是可能影响编译器优化的常量。

        我对方法做了一些事情,也反对编译器优化。

        public class Test {
        
        
            static int n = 100_000_000;
            static int[] all_string = new int[n*1*5];
            static int all_strings_index = 0;
        
        
        
            public static void main(String[] args) {
        
                while(true) {
            
                    all_strings_index = 0;
            
                    int s1 = (int) System.nanoTime();
                    int s2 = (int) System.nanoTime();
                    int s3 = (int) System.nanoTime();
                    int s4 = (int) System.nanoTime();
                    int s5 = (int) System.nanoTime();
            
                    long t = System.currentTimeMillis();
            
            
                    t = System.currentTimeMillis();
                    for (int i = 0; i < n; i++) {
                        var(s1, s2, s3, s4, s5);
                    }
                    System.err.println("varargs    "+(System.currentTimeMillis() - t));
            
                    all_strings_index = 0;
            
                    t = System.currentTimeMillis();
                    for (int i = 0; i < n; i++) {
                        par(s1, s2, s3, s4, s5);
                    }
                    System.err.println("parameters "+(System.currentTimeMillis() - t));
            
            
                    all_strings_index = 0;
            
                    int[] arr = new int[] {s1, s2, s3, s4, s5};
            
                    t = System.currentTimeMillis();
                    for (int i = 0; i < n; i++) {
                        var2(arr);
                    }
                    System.err.println("array      "+(System.currentTimeMillis() - t));
                    System.err.println();
            
                }
        
            }
        
            static void par(int a1, int a2, int a3, int a4, int a5) {
                all_string[all_strings_index++] = a1;
                all_string[all_strings_index++] = a2;
                all_string[all_strings_index++] = a3;
                all_string[all_strings_index++] = a4;
                all_string[all_strings_index++] = a5;
            }
        
            static void var(int... a) {
                for (int s : a) {
                    all_string[all_strings_index++] = s;
                }
            }
        
            static void var2(int[] a) {
                for (int s : a) {
                    all_string[all_strings_index++] = s;
                }
            }
        }
        
        varargs    981
        parameters 415
        array      687
        
        varargs    962
        parameters 434
        array      411
        
        varargs    975
        parameters 469
        array      439
        
        varargs    983
        parameters 462
        array      447
        
        varargs    999
        parameters 470
        array      439
        
        varargs    1018
        parameters 475
        array      455
        
        varargs    1014
        parameters 467
        array      440
        

        可变参数要慢很多。

        【讨论】:

          【解决方案5】:

          如前所述,使用 var-args 时会维护一个数组...,

          您还应该尝试查看在每个方法的参数中添加“final”的影响

          我个人从 2250 -> 2234 ms 的阵列得到了改进。

          【讨论】:

          • Java 中的参数确实不能定为最终的,它们仍然可以更改,所以这应该不会影响任何事情。
          • 如何更改最终参数?
          • 最终参数和变量无法更改。我认为这一点更好地说明:使 object reference final 意味着您不能更改引用,但这并不意味着它引用的对象不能更改状态。
          • 换句话说,对于原始类型,您无法更改它们,对于对象使用 setter 或更改其公共成员。
          【解决方案6】:

          javap -v Test.class 生成以下文本:

          Classfile /Users/jing/code/github/my/java-projects/byte-code/test.class
            Last modified 2021-9-5; size 1758 bytes
            MD5 checksum 007113ca45d82f5f86a65725f28a560f
            Compiled from "Test.java"
          public class Test
            minor version: 0
            major version: 52
            flags: ACC_PUBLIC, ACC_SUPER
          Constant pool:
             #1 = Methodref          #23.#46        // java/lang/Object."<init>":()V
             #2 = Fieldref           #22.#47        // Test.all_strings_index:I
             #3 = Methodref          #48.#49        // java/lang/System.nanoTime:()J
             #4 = Methodref          #48.#50        // java/lang/System.currentTimeMillis:()J
             #5 = Fieldref           #22.#51        // Test.n:I
             #6 = Methodref          #22.#52        // Test.var:([I)V
             #7 = Fieldref           #48.#53        // java/lang/System.err:Ljava/io/PrintStream;
             #8 = Class              #54            // java/lang/StringBuilder
             #9 = Methodref          #8.#46         // java/lang/StringBuilder."<init>":()V
            #10 = String             #55            // varargs
            #11 = Methodref          #8.#56         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            #12 = Methodref          #8.#57         // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
            #13 = Methodref          #8.#58         // java/lang/StringBuilder.toString:()Ljava/lang/String;
            #14 = Methodref          #59.#60        // java/io/PrintStream.println:(Ljava/lang/String;)V
            #15 = Methodref          #22.#61        // Test.par:(IIIII)V
            #16 = String             #62            // parameters
            #17 = Methodref          #22.#63        // Test.var2:([I)V
            #18 = String             #64            // array
            #19 = Methodref          #59.#65        // java/io/PrintStream.println:()V
            #20 = Fieldref           #22.#66        // Test.all_string:[I
            #21 = Integer            100000000
            #22 = Class              #67            // Test
            #23 = Class              #68            // java/lang/Object
            #24 = Utf8               n
            #25 = Utf8               I
            #26 = Utf8               all_string
            #27 = Utf8               [I
            #28 = Utf8               all_strings_index
            #29 = Utf8               <init>
            #30 = Utf8               ()V
            #31 = Utf8               Code
            #32 = Utf8               LineNumberTable
            #33 = Utf8               main
            #34 = Utf8               ([Ljava/lang/String;)V
            #35 = Utf8               StackMapTable
            #36 = Class              #69            // "[Ljava/lang/String;"
            #37 = Class              #27            // "[I"
            #38 = Utf8               par
            #39 = Utf8               (IIIII)V
            #40 = Utf8               var
            #41 = Utf8               ([I)V
            #42 = Utf8               var2
            #43 = Utf8               <clinit>
            #44 = Utf8               SourceFile
            #45 = Utf8               Test.java
            #46 = NameAndType        #29:#30        // "<init>":()V
            #47 = NameAndType        #28:#25        // all_strings_index:I
            #48 = Class              #70            // java/lang/System
            #49 = NameAndType        #71:#72        // nanoTime:()J
            #50 = NameAndType        #73:#72        // currentTimeMillis:()J
            #51 = NameAndType        #24:#25        // n:I
            #52 = NameAndType        #40:#41        // var:([I)V
            #53 = NameAndType        #74:#75        // err:Ljava/io/PrintStream;
            #54 = Utf8               java/lang/StringBuilder
            #55 = Utf8               varargs
            #56 = NameAndType        #76:#77        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            #57 = NameAndType        #76:#78        // append:(J)Ljava/lang/StringBuilder;
            #58 = NameAndType        #79:#80        // toString:()Ljava/lang/String;
            #59 = Class              #81            // java/io/PrintStream
            #60 = NameAndType        #82:#83        // println:(Ljava/lang/String;)V
            #61 = NameAndType        #38:#39        // par:(IIIII)V
            #62 = Utf8               parameters
            #63 = NameAndType        #42:#41        // var2:([I)V
            #64 = Utf8               array
            #65 = NameAndType        #82:#30        // println:()V
            #66 = NameAndType        #26:#27        // all_string:[I
            #67 = Utf8               Test
            #68 = Utf8               java/lang/Object
            #69 = Utf8               [Ljava/lang/String;
            #70 = Utf8               java/lang/System
            #71 = Utf8               nanoTime
            #72 = Utf8               ()J
            #73 = Utf8               currentTimeMillis
            #74 = Utf8               err
            #75 = Utf8               Ljava/io/PrintStream;
            #76 = Utf8               append
            #77 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
            #78 = Utf8               (J)Ljava/lang/StringBuilder;
            #79 = Utf8               toString
            #80 = Utf8               ()Ljava/lang/String;
            #81 = Utf8               java/io/PrintStream
            #82 = Utf8               println
            #83 = Utf8               (Ljava/lang/String;)V
          {
            static int n;
              descriptor: I
              flags: ACC_STATIC
          
            static int[] all_string;
              descriptor: [I
              flags: ACC_STATIC
          
            static int all_strings_index;
              descriptor: I
              flags: ACC_STATIC
          
            public Test();
              descriptor: ()V
              flags: ACC_PUBLIC
              Code:
                stack=1, locals=1, args_size=1
                   0: aload_0
                   1: invokespecial #1                  // Method java/lang/Object."<init>":()V
                   4: return
                LineNumberTable:
                  line 1: 0
          
            public static void main(java.lang.String[]);
              descriptor: ([Ljava/lang/String;)V
              flags: ACC_PUBLIC, ACC_STATIC
              Code:
                stack=6, locals=10, args_size=1
                   0: iconst_0
                   1: putstatic     #2                  // Field all_strings_index:I
                   4: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
                   7: l2i
                   8: istore_1
                   9: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
                  12: l2i
                  13: istore_2
                  14: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
                  17: l2i
                  18: istore_3
                  19: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
                  22: l2i
                  23: istore        4
                  25: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
                  28: l2i
                  29: istore        5
                  31: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                  34: lstore        6
                  36: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                  39: lstore        6
                  41: iconst_0
                  42: istore        8
                  44: iload         8
                  46: getstatic     #5                  // Field n:I
                  49: if_icmpge     86
                  52: iconst_5
                  53: newarray       int
                  55: dup
                  56: iconst_0
                  57: iload_1
                  58: iastore
                  59: dup
                  60: iconst_1
                  61: iload_2
                  62: iastore
                  63: dup
                  64: iconst_2
                  65: iload_3
                  66: iastore
                  67: dup
                  68: iconst_3
                  69: iload         4
                  71: iastore
                  72: dup
                  73: iconst_4
                  74: iload         5
                  76: iastore
                  77: invokestatic  #6                  // Method var:([I)V
                  80: iinc          8, 1
                  83: goto          44
                  86: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
                  89: new           #8                  // class java/lang/StringBuilder
                  92: dup
                  93: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
                  96: ldc           #10                 // String varargs
                  98: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                 101: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                 104: lload         6
                 106: lsub
                 107: invokevirtual #12                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
                 110: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
                 113: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
                 116: iconst_0
                 117: putstatic     #2                  // Field all_strings_index:I
                 120: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                 123: lstore        6
                 125: iconst_0
                 126: istore        8
                 128: iload         8
                 130: getstatic     #5                  // Field n:I
                 133: if_icmpge     152
                 136: iload_1
                 137: iload_2
                 138: iload_3
                 139: iload         4
                 141: iload         5
                 143: invokestatic  #15                 // Method par:(IIIII)V
                 146: iinc          8, 1
                 149: goto          128
                 152: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
                 155: new           #8                  // class java/lang/StringBuilder
                 158: dup
                 159: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
                 162: ldc           #16                 // String parameters
                 164: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                 167: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                 170: lload         6
                 172: lsub
                 173: invokevirtual #12                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
                 176: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
                 179: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
                 182: iconst_0
                 183: putstatic     #2                  // Field all_strings_index:I
                 186: iconst_5
                 187: newarray       int
                 189: dup
                 190: iconst_0
                 191: iload_1
                 192: iastore
                 193: dup
                 194: iconst_1
                 195: iload_2
                 196: iastore
                 197: dup
                 198: iconst_2
                 199: iload_3
                 200: iastore
                 201: dup
                 202: iconst_3
                 203: iload         4
                 205: iastore
                 206: dup
                 207: iconst_4
                 208: iload         5
                 210: iastore
                 211: astore        8
                 213: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                 216: lstore        6
                 218: iconst_0
                 219: istore        9
                 221: iload         9
                 223: getstatic     #5                  // Field n:I
                 226: if_icmpge     240
                 229: aload         8
                 231: invokestatic  #17                 // Method var2:([I)V
                 234: iinc          9, 1
                 237: goto          221
                 240: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
                 243: new           #8                  // class java/lang/StringBuilder
                 246: dup
                 247: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
                 250: ldc           #18                 // String array
                 252: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                 255: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
                 258: lload         6
                 260: lsub
                 261: invokevirtual #12                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
                 264: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
                 267: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
                 270: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
                 273: invokevirtual #19                 // Method java/io/PrintStream.println:()V
                 276: goto          0
                LineNumberTable:
                  line 13: 0
                  line 15: 4
                  line 16: 9
                  line 17: 14
                  line 18: 19
                  line 19: 25
                  line 21: 31
                  line 24: 36
                  line 25: 41
                  line 26: 52
                  line 25: 80
                  line 28: 86
                  line 30: 116
                  line 32: 120
                  line 33: 125
                  line 34: 136
                  line 33: 146
                  line 36: 152
                  line 39: 182
                  line 41: 186
                  line 43: 213
                  line 44: 218
                  line 45: 229
                  line 44: 234
                  line 47: 240
                  line 48: 270
                  line 50: 276
                StackMapTable: number_of_entries = 7
                  frame_type = 0 /* same */
                  frame_type = 255 /* full_frame */
                    offset_delta = 43
                    locals = [ class "[Ljava/lang/String;", int, int, int, int, int, long, int ]
                    stack = []
                  frame_type = 250 /* chop */
                    offset_delta = 41
                  frame_type = 252 /* append */
                    offset_delta = 41
                    locals = [ int ]
                  frame_type = 250 /* chop */
                    offset_delta = 23
                  frame_type = 253 /* append */
                    offset_delta = 68
                    locals = [ class "[I", int ]
                  frame_type = 250 /* chop */
                    offset_delta = 18
          
            static void par(int, int, int, int, int);
              descriptor: (IIIII)V
              flags: ACC_STATIC
              Code:
                stack=4, locals=5, args_size=5
                   0: getstatic     #20                 // Field all_string:[I
                   3: getstatic     #2                  // Field all_strings_index:I
                   6: dup
                   7: iconst_1
                   8: iadd
                   9: putstatic     #2                  // Field all_strings_index:I
                  12: iload_0
                  13: iastore
                  14: getstatic     #20                 // Field all_string:[I
                  17: getstatic     #2                  // Field all_strings_index:I
                  20: dup
                  21: iconst_1
                  22: iadd
                  23: putstatic     #2                  // Field all_strings_index:I
                  26: iload_1
                  27: iastore
                  28: getstatic     #20                 // Field all_string:[I
                  31: getstatic     #2                  // Field all_strings_index:I
                  34: dup
                  35: iconst_1
                  36: iadd
                  37: putstatic     #2                  // Field all_strings_index:I
                  40: iload_2
                  41: iastore
                  42: getstatic     #20                 // Field all_string:[I
                  45: getstatic     #2                  // Field all_strings_index:I
                  48: dup
                  49: iconst_1
                  50: iadd
                  51: putstatic     #2                  // Field all_strings_index:I
                  54: iload_3
                  55: iastore
                  56: getstatic     #20                 // Field all_string:[I
                  59: getstatic     #2                  // Field all_strings_index:I
                  62: dup
                  63: iconst_1
                  64: iadd
                  65: putstatic     #2                  // Field all_strings_index:I
                  68: iload         4
                  70: iastore
                  71: return
                LineNumberTable:
                  line 55: 0
                  line 56: 14
                  line 57: 28
                  line 58: 42
                  line 59: 56
                  line 60: 71
          
            static void var(int...);
              descriptor: ([I)V
              flags: ACC_STATIC, ACC_VARARGS
              Code:
                stack=4, locals=5, args_size=1
                   0: aload_0
                   1: astore_1
                   2: aload_1
                   3: arraylength
                   4: istore_2
                   5: iconst_0
                   6: istore_3
                   7: iload_3
                   8: iload_2
                   9: if_icmpge     38
                  12: aload_1
                  13: iload_3
                  14: iaload
                  15: istore        4
                  17: getstatic     #20                 // Field all_string:[I
                  20: getstatic     #2                  // Field all_strings_index:I
                  23: dup
                  24: iconst_1
                  25: iadd
                  26: putstatic     #2                  // Field all_strings_index:I
                  29: iload         4
                  31: iastore
                  32: iinc          3, 1
                  35: goto          7
                  38: return
                LineNumberTable:
                  line 63: 0
                  line 64: 17
                  line 63: 32
                  line 66: 38
                StackMapTable: number_of_entries = 2
                  frame_type = 254 /* append */
                    offset_delta = 7
                    locals = [ class "[I", int, int ]
                  frame_type = 248 /* chop */
                    offset_delta = 30
          
            static void var2(int[]);
              descriptor: ([I)V
              flags: ACC_STATIC
              Code:
                stack=4, locals=5, args_size=1
                   0: aload_0
                   1: astore_1
                   2: aload_1
                   3: arraylength
                   4: istore_2
                   5: iconst_0
                   6: istore_3
                   7: iload_3
                   8: iload_2
                   9: if_icmpge     38
                  12: aload_1
                  13: iload_3
                  14: iaload
                  15: istore        4
                  17: getstatic     #20                 // Field all_string:[I
                  20: getstatic     #2                  // Field all_strings_index:I
                  23: dup
                  24: iconst_1
                  25: iadd
                  26: putstatic     #2                  // Field all_strings_index:I
                  29: iload         4
                  31: iastore
                  32: iinc          3, 1
                  35: goto          7
                  38: return
                LineNumberTable:
                  line 69: 0
                  line 70: 17
                  line 69: 32
                  line 72: 38
                StackMapTable: number_of_entries = 2
                  frame_type = 254 /* append */
                    offset_delta = 7
                    locals = [ class "[I", int, int ]
                  frame_type = 248 /* chop */
                    offset_delta = 30
          
            static {};
              descriptor: ()V
              flags: ACC_STATIC
              Code:
                stack=2, locals=0, args_size=0
                   0: ldc           #21                 // int 100000000
                   2: putstatic     #5                  // Field n:I
                   5: getstatic     #5                  // Field n:I
                   8: iconst_1
                   9: imul
                  10: iconst_5
                  11: imul
                  12: newarray       int
                  14: putstatic     #20                 // Field all_string:[I
                  17: iconst_0
                  18: putstatic     #2                  // Field all_strings_index:I
                  21: return
                LineNumberTable:
                  line 3: 0
                  line 4: 5
                  line 5: 17
          }
          SourceFile: "Test.java"
          

          检查上述javap输出后,可以得出以下结论。

          在方法定义方面:

          • 要将参数加载到操作数堆栈,par 使用 iloadvar(int...) 使用 iaload。执行iaload 需要比iload 更多的指令来准备操作数堆栈。
          • par 按顺序执行其所有指令。 var(int...) 有跳跃。

          par的定义:

            static void par(int, int, int, int, int);
              descriptor: (IIIII)V
              flags: ACC_STATIC
              Code:
                stack=4, locals=5, args_size=5
                   0: getstatic     #20                 // Field all_string:[I
                   3: getstatic     #2                  // Field all_strings_index:I
                   6: dup
                   7: iconst_1
                   8: iadd
                   9: putstatic     #2                  // Field all_strings_index:I
                  12: iload_0
                  13: iastore
                  14: getstatic     #20                 // Field all_string:[I
                  17: getstatic     #2                  // Field all_strings_index:I
                  20: dup
                  21: iconst_1
                  22: iadd
                  23: putstatic     #2                  // Field all_strings_index:I
                  26: iload_1
                  27: iastore
                  28: getstatic     #20                 // Field all_string:[I
                  31: getstatic     #2                  // Field all_strings_index:I
                  34: dup
                  35: iconst_1
                  36: iadd
                  37: putstatic     #2                  // Field all_strings_index:I
                  40: iload_2
                  41: iastore
                  42: getstatic     #20                 // Field all_string:[I
                  45: getstatic     #2                  // Field all_strings_index:I
                  48: dup
                  49: iconst_1
                  50: iadd
                  51: putstatic     #2                  // Field all_strings_index:I
                  54: iload_3
                  55: iastore
                  56: getstatic     #20                 // Field all_string:[I
                  59: getstatic     #2                  // Field all_strings_index:I
                  62: dup
                  63: iconst_1
                  64: iadd
                  65: putstatic     #2                  // Field all_strings_index:I
                  68: iload         4
                  70: iastore
                  71: return
                LineNumberTable:
                  line 55: 0
                  line 56: 14
                  line 57: 28
                  line 58: 42
                  line 59: 56
                  line 60: 71
          

          var(int...)的定义:

            static void var(int...);
              descriptor: ([I)V
              flags: ACC_STATIC, ACC_VARARGS
              Code:
                stack=4, locals=5, args_size=1
                   0: aload_0
                   1: astore_1
                   2: aload_1
                   3: arraylength
                   4: istore_2
                   5: iconst_0
                   6: istore_3
                   7: iload_3
                   8: iload_2
                   9: if_icmpge     38
                  12: aload_1
                  13: iload_3
                  14: iaload
                  15: istore        4
                  17: getstatic     #20                 // Field all_string:[I
                  20: getstatic     #2                  // Field all_strings_index:I
                  23: dup
                  24: iconst_1
                  25: iadd
                  26: putstatic     #2                  // Field all_strings_index:I
                  29: iload         4
                  31: iastore
                  32: iinc          3, 1
                  35: goto          7
                  38: return
                LineNumberTable:
                  line 63: 0
                  line 64: 17
                  line 63: 32
                  line 66: 38
                StackMapTable: number_of_entries = 2
                  frame_type = 254 /* append */
                    offset_delta = 7
                    locals = [ class "[I", int, int ]
                  frame_type = 248 /* chop */
                    offset_delta = 30
          

          在方法调用方面:

          • par 使用iload 指令将局部变量加载到操作数堆栈中。 var(int...)首先创建一个数组,并将局部变量存储到数组中。后者比前者需要更多的指导。

          par 的调用:

                 136: iload_1
                 137: iload_2
                 138: iload_3
                 139: iload         4
                 141: iload         5
                 143: invokestatic  #15                 // Method par:(IIIII)V
          

          var(int...) 的调用:

                  52: iconst_5
                  53: newarray       int
                  55: dup
                  56: iconst_0
                  57: iload_1
                  58: iastore
                  59: dup
                  60: iconst_1
                  61: iload_2
                  62: iastore
                  63: dup
                  64: iconst_2
                  65: iload_3
                  66: iastore
                  67: dup
                  68: iconst_3
                  69: iload         4
                  71: iastore
                  72: dup
                  73: iconst_4
                  74: iload         5
                  76: iastore
                  77: invokestatic  #6                  // Method var:([I)V
          
          

          以上结论与https://stackoverflow.com/a/63121156/431698中的测试结果一致。而 varargs 的性能问题是为什么一些流行的日志库(例如 Log4j2Flogger)除了 varargs 版本之外还有明确列出消息参数的版本。

          Flogger的LoggingApi有以下方法:

          void log(String msg);
          void log(String msg, @NullableDecl Object p1);
          ...
          void log(
              String msg,
              @NullableDecl Object p1,
              @NullableDecl Object p2,
              @NullableDecl Object p3,
              @NullableDecl Object p4,
              @NullableDecl Object p5,
              @NullableDecl Object p6,
              @NullableDecl Object p7,
              @NullableDecl Object p8,
              @NullableDecl Object p9,
              @NullableDecl Object p10);
          void log(
              String msg,
              @NullableDecl Object p1,
              @NullableDecl Object p2,
              @NullableDecl Object p3,
              @NullableDecl Object p4,
              @NullableDecl Object p5,
              @NullableDecl Object p6,
              @NullableDecl Object p7,
              @NullableDecl Object p8,
              @NullableDecl Object p9,
              @NullableDecl Object p10,
              Object... rest);
          

          Log4j2的Logger有以下方法:

          void info(String message)
          void info(String message, Object p0)
          void info(String message, Object p0, Object p1)
          ...
          void info(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9)
          void info(String message, Object... params)
          

          有关 Log4j2 的可变参数处理的讨论,请参阅Why does Logger class provide a bunch of info methods beside the varargs mthod?

          【讨论】:

            猜你喜欢
            • 2013-09-29
            • 2015-05-08
            • 1970-01-01
            • 1970-01-01
            • 2011-07-04
            • 1970-01-01
            • 2011-01-21
            • 2014-05-19
            • 1970-01-01
            相关资源
            最近更新 更多