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 使用 iload。 var(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 的性能问题是为什么一些流行的日志库(例如 Log4j2 和 Flogger)除了 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?