JVM字节码分析--switch语法糖
一、switch 底层实现
编译器使用 tableswitch 和 lookupswitch 两个指令来生成 switch 语句的编译代码:
int chooseNear(int i) {
switch (i) {
case 0: return 0;
case 1: return 1;
case 4: return 4;
default: return -1;
}
}
字节码如下:
0: iload_1
1: tableswitch { // 100 to 104
0: 36
1: 38
2: 42
3: 42
4: 40
default: 42
}
可见在1到4之间字节码填充了虚假的 2、3 方便 tableswitch 通过计算偏移量即可一次找到对应的case 。比如case为1时,只需计算1 - 0 = 1即可得到偏移量为1,从 tableswitch 中取出下标为 1 的case 即可,此时算法复杂度为O(1)。
但 tableswitch 只适用于case值紧凑的情况,若case值相差太大,JVM 则自动使用 lookupswitch 指令生成编译代码:
int chooseFar(int i) {
switch (i) {
case 2: return 1;
case 20: return 10;
case 300: return 100;
default: return -1;
}
}
字节码如下:
0: iload_1
1: lookupswitch { // 3
2: 36
20: 38
300: 41
default: 44
}
2、20、300相差较大,使用 lookupswitch 指令实际上通过二分查找的方式寻找对应的 case ,此时算法复杂度为 O(logn)
二、String的switch实现
String 的 switch 在 JDK7 中引入, 其实现流程分为以下几步:
- 计算字符串 hashCode
- 使用 lookupswitch 对整型 hashCode 进行分支
- 对相同 hashCode 值的字符串进行最后的字符串匹配
- 执行 case 块代码
例如下面的代码:
public int test(String name) {
switch (name) {
case "Java":
return 100;
case "Kotlin":
return 200;
default:
return -1;
}
}
字节码如下:
0: aload_1
1: astore_2
2: iconst_m1
3: istore_3
4: aload_2
5: invokevirtual #2 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 2
-2041707231: 50 // 对应 "Kotlin".hashCode()
2301506: 36 // 对应 "Java".hashCode()
default: 61
}
36: aload_2
37: ldc #3 // String Java
39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 61
45: iconst_0
46: istore_3
47: goto 61
50: aload_2
51: ldc #5 // String Kotlin
53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 61
59: iconst_1
60: istore_3
61: iload_3
62: lookupswitch { // 2
0: 88
1: 91
default: 95
}
// 88 ~ 90
88: bipush 100
90: ireturn
91: sipush 200
94: ireturn
95: iconst_m1
96: ireturn
经过字节码翻译,实际上代码可翻译为:
public int test_translate(String name) {
//对应0--3
String tmpName = name;
int matchIndex = -1;
//对应4--60
switch (tmpName.hashCode()) {
case -2041707231:
if (tmpName.equals("Kotlin")) {
matchIndex = 1;
}
break;
case 2301506:
if (tmpName.equals("Java")) {
matchIndex = 0;
}
break;
default:
break;
}
//对应61--96
switch (matchIndex) {
case 0:
return 100;
case 1:
return 200;
default:
return -1;
}
}