【发布时间】:2014-10-23 11:41:07
【问题描述】:
我阅读了关于编译开关的JVM specification,并对如何编译 String 上的 switch 语句产生了兴趣。这是我检查的测试方法(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;
}
}
我希望这个方法可以编译成简单的lookupswitch on hashCode的字符串,但是突然
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
如您所见,在第一个 lookupswitch 的分支中,JVM 并没有做真正的工作,而是为后续的 tableswitch(第 84 行)生成索引。
Tableswitch 应该工作得很快,所以不会带来很多额外的工作。但是无论如何,生成额外开关的目的是什么?
更新
我了解 hashCode 冲突的可能性。我想说的是,编译器可以将所有实际工作从后续 tableswitch 移到 first,然后使用 ifeq 跳转到所有 switch 分支的末尾,而不是后续的 tableswitch。 所以我在这里看到的一个可能的答案是:在第一个 switch 编译器尝试根据已知数量的情况为 ifeq 跳转预先计算标签,但我不确定这是唯一的原因。
更新2
正如 @ericbn 所建议的,我尝试编译
switch (i) {
case 97: return -100;
case 51713: return 1;
case 99: return 2;
default: return -1;
}
将 i 作为 int,编译器给了我简单的查找开关。
【问题讨论】:
-
由于鸽巢原理,不同的字符串可以具有相同的 hashCode。我认为这就是它进行两次切换的原因。
-
你不认为只有一个开关吗?第二个开关用于条件返回。
-
@HuStmpHrrr 第二个开关引入了一个明显没有动机的间接层,从表中查找的跳转可以直接插入到第一个开关块中作为
goto目标。 -
@HuStmpHrrr 我试图修改代码并且不要在 switch 中使用 return 语句。无论如何,编译成两个开关。此外,如果我在整数上使用开关,则编译为一个。
-
试着用 int:
switch (i) { case 97: return -100; case 51713: return 1; case 99: return 2; default: return -1; }查看 switch 的编译代码,并检查生成的“switches”...
标签: java jvm switch-statement java-7 bytecode