为什么打开 String 会编译成两个开关
2022-09-02 04:30:33
我阅读了有关编译开关的JVM规范,并对如何编译String上的开关语句产生了兴趣。以下是我检查的测试方法(JDK1.7.0_40):
static int test(String i) {
switch (i) {
case "a": return -100;
case "45b": return 1;
case "c": return 2;
default: return -1;
}
}
我希望这种方法被编译成简单的查找开关在字符串的哈希代码上,但突然之间
static int test(java.lang.String);
Code:
0: aload_0
1: astore_1
2: iconst_m1
3: istore_2
4: aload_1
5: invokevirtual #6 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 3
97: 44
99: 72
51713: 58
default: 83
}
44: aload_1
45: ldc #7 // String a
47: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
50: ifeq 83
53: iconst_0
54: istore_2
55: goto 83
58: aload_1
59: ldc #9 // String 45b
61: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
64: ifeq 83
67: iconst_1
68: istore_2
69: goto 83
72: aload_1
73: ldc #10 // String c
75: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
78: ifeq 83
81: iconst_2
82: istore_2
83: iload_2
84: tableswitch { // 0 to 2
0: 112
1: 115
2: 117
default: 119
}
112: bipush -100
114: ireturn
115: iconst_1
116: ireturn
117: iconst_2
118: ireturn
119: iconst_m1
120: ireturn
如您所见,在第一个查找开关的分支中,JVM 并没有为后续表开关(第 84 行)生成索引。
桌面开关应该工作得很快,所以不会带来很多额外的工作。但无论如何,生成额外开关的目的是什么?
更新
我了解哈希代码冲突的可能性。我想说的是,编译器可以将所有实际工作从后续的表开关移动到第一个,然后使用ifeq跳转到所有开关分支的末尾,而不是后续的表开关开关。因此,我在这里看到的一个可能的答案是:在第一个开关编译器中,编译器会尝试根据已知的情况数预先计算ifeq跳转的标签,但我不确定这是唯一的原因。
Update2
按照@ericbn建议,我试图编译
switch (i) {
case 97: return -100;
case 51713: return 1;
case 99: return 2;
default: return -1;
}
用 i as int 和 编译器给了我简单的查找开关。