【发布时间】:2020-04-17 22:32:43
【问题描述】:
我想你们中的大多数人都知道goto 是 Java 语言中的保留关键字,但实际上并未使用。您可能还知道goto 是一个Java 虚拟机(JVM)操作码。我认为 Java、Scala 和 Kotlin 的所有复杂控制流结构在 JVM 级别都是使用 goto 和 ifeq、ifle、iflt 等的某种组合来实现的。
查看 JVM 规范 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w 我看到还有一个 goto_w 操作码。 goto 采用 2 字节的分支偏移量,goto_w 采用 4 字节的分支偏移量。规范指出
虽然 goto_w 指令采用 4 字节的分支偏移量,但其他因素将方法的大小限制为 65535 字节(第 4.11 节)。在 Java 虚拟机的未来版本中可能会提高此限制。
在我看来,goto_w 是面向未来的,就像其他一些 *_w 操作码一样。但我也想到,也许goto_w 可以与两个较高的有效字节清零和两个较低的有效字节与goto 相同,并根据需要进行调整。
例如,给定这个 Java Switch-Case(或 Scala Match-Case):
12: lookupswitch {
112785: 48 // case "red"
3027034: 76 // case "green"
98619139: 62 // case "blue"
default: 87
}
48: aload_2
49: ldc #17 // String red
51: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
54: ifeq 87
57: iconst_0
58: istore_3
59: goto 87
62: aload_2
63: ldc #19 // String green
65: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
68: ifeq 87
71: iconst_1
72: istore_3
73: goto 87
76: aload_2
77: ldc #20 // String blue
79: invokevirtual #18
// etc.
我们可以改写成
12: lookupswitch {
112785: 48
3027034: 78
98619139: 64
default: 91
}
48: aload_2
49: ldc #17 // String red
51: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
54: ifeq 91 // 00 5B
57: iconst_0
58: istore_3
59: goto_w 91 // 00 00 00 5B
64: aload_2
65: ldc #19 // String green
67: invokevirtual #18
// Method java/lang/String.equals:(Ljava/lang/Object;)Z
70: ifeq 91
73: iconst_1
74: istore_3
75: goto_w 91
79: aload_2
81: ldc #20 // String blue
83: invokevirtual #18
// etc.
我实际上并没有尝试过,因为我可能犯了一个错误,更改“行号”以适应 goto_ws。不过既然在spec里面,应该是可以做到的。
我的问题是编译器或其他字节码生成器是否有理由使用具有当前 65535 限制的 goto_w,而不是表明它可以完成?
【问题讨论】: