javap -v Test.class
produces the following text:
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"
After checking the above javap output, the following conclusion can be drawn.
On the method definition side:
- For loading parameters to the operand stack,
par
use iload
. var(int...)
use iaload
. Executing iaload
requires more instructions to prepare the operand stack than iload
.
par
executes all its instruction sequentially. var(int...)
has jumps.
par
's definition:
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...)
's definition:
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
On the method invocation side:
par
load the local variables to the operand stack with iload
instruction. var(int...)
first create an array and store the local variables to the array. The latter needs more instruction than the former.
par
's invocation:
136: iload_1
137: iload_2
138: iload_3
139: iload 4
141: iload 5
143: invokestatic #15 // Method par:(IIIII)V
var(int...)
's invocation:
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
The above conclusion is in accordance with the test result in https://stackoverflow.com/a/63121156/431698. And varargs performance problem is why some popular logging libraries such as Log4j2 and Flogger have versions that explicitly list message parameters besides the varargs version.
Flogger's LoggingApi has the following methods:
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's Logger has the following methods:
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)
For a discussion about Log4j2's varargs handling, see Why does Logger class provide a bunch of info methods beside the varargs mthod?