Java 为什么只为*const\n JVM指令定义这样的常量范围?

Java 为什么只为*const\n JVM指令定义这样的常量范围?,java,jvm,bytecode,jvm-bytecode,Java,Jvm,Bytecode,Jvm Bytecode,根据JVM的说法,有几个指令经过优化,可以使用一组特定的常量。 有人能解释为什么只定义了这个常数范围吗 iconst_n:push整数常量n,0≤ N≤ 5 lconst\u n:推长常数n,0≤ N≤ 1 fconst\u n:推动浮点常量n,0≤ N≤ 2 dconst\u n:推双常数n,0≤ N≤ 1 我假设这是由于这些常数的使用频率,但是 我找不到我的想法或任何其他信息的确认 有人能解释为什么只定义了这个常数范围吗 这在当时似乎是个好主意。字节码基于较旧的虚拟机实现,可能继承了这些

根据JVM的说法,有几个指令经过优化,可以使用一组特定的常量。 有人能解释为什么只定义了这个常数范围吗

  • iconst_n:push整数常量n,0≤ N≤ 5
  • lconst\u n:推长常数n,0≤ N≤ 1
  • fconst\u n:推动浮点常量n,0≤ N≤ 2
  • dconst\u n:推双常数n,0≤ N≤ 1
我假设这是由于这些常数的使用频率,但是 我找不到我的想法或任何其他信息的确认

有人能解释为什么只定义了这个常数范围吗

这在当时似乎是个好主意。字节码基于较旧的虚拟机实现,可能继承了这些约束

我假设这是由于这些常数的使用频率


我做了一些研究,研究了不同指令在发布几年后的使用频率,发现几乎没有证据表明有经验方法可以决定哪些指令是1字节还是2字节。然后,在编写原始设计时,几乎没有生成字节码。

明确提到了意图,例如:

Java虚拟机经常利用某些操作数的可能性(对于iconst_________________________________________________。因为iconst_0指令知道它将推送一个
int
0
,iconst_0不需要存储操作数来告诉它要推送什么值,也不需要获取或解码操作数。将推送0编译为bipush 0是正确的,但会使编译后的
spin
1的代码长一个字节。一个简单的虚拟机每次在循环中提取和解码显式操作数时也会花费额外的时间。隐式操作数的使用使编译后的代码更加紧凑和高效

这是正在讨论的示例代码,即从0到100的for循环

这并不是唯一的优化,例如,对于堆栈框架中的局部变量,有专门的指令来访问第一个

这些操作在指令集中也有特殊的支持。在
spin
中,使用istore_1和iload_1指令将值传递给局部变量或从局部变量传递值,每个指令都隐式地对局部变量1进行操作

还请注意,存在方便的指令iinc,它是唯一直接操作局部变量的指令。因此,循环计数,通常从0或1开始,并将计数器增加一个小值,如1,是这些优化指令的主要用例

小变量索引的优化是合理的,因为这些索引是按升序分配的,
this
(如果不是
static
),后面是参数,后面是第一个局部变量。原则上,编译器可以通过对变量重新排序,使其在优化的索引处具有最常用的变量,从而进一步优化该索引,但在实践中,这种情况不会发生

对于像HotSpot这样的优化JVM,使用这些指令肯定没有性能优势,但它们仍然会使字节码稍微缩短