为什么只为* const_n JVM指令定义了这样的常量范围?

时间:2018-10-24 11:26:28

标签: java jvm bytecode jvm-bytecode

根据JVM specification,有一些指令已针对使用一组特定的常量进行了优化。 谁能解释为什么只定义了这个常数范围?

  • iconst_n:推入整数常量n, 0≤n≤5
  • lconst_n:推长常量n, 0≤n≤1
  • fconst_n:推入浮点常量n, 0≤n≤2
  • dconst_n:推入双常量n, 0≤n≤1

我认为这是由于使用这些常数的频率,但是 我找不到我的想法或有关它的任何其他信息的确认。

2 个答案:

答案 0 :(得分:2)

  

谁能解释为什么只定义了这一范围的常数?

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

  

我认为这是由于这些常数的使用频率所致,

我根据出现后几年使用不同指令的频率进行了一些研究,发现很少有经验方法来确定哪些指令是1字节还是2字节。再说一次,在编写原始设计时,几乎没有生成字节码。

答案 1 :(得分:1)

已经明确提及了该意图,例如在JVMS, §3.2. Use of Constants, Local Variables, and Control Constructs中:

  

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

¹这是正在讨论的示例代码,即for循环从零到一百

这不是此类的唯一优化,例如有一些特殊的说明可用于访问堆栈帧中局部变量的第一个。

  

这些操作在指令集中也有特殊的支持。在spin中,使用 istore_1 iload_1 指令在局部变量之间传递值,每个指令隐式地对局部变量 1

还要注意便捷指令 iinc 的存在,它是唯一直接对局部变量进行运算的指令。因此,计数循环通常是从零或一开始,然后将计数器增加一个类似一个小的值,这是这些优化指令的主要用例。

小变量索引的优化是合理的,因为这些索引是按升序分配的,this(如果不是static,则是参数,其后是第一个局部变量。原则上,编译器可以通过将变量重新排序以使最常用的变量位于优化索引处来进一步优化此变量,但是实际上,这不会发生。

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