为什么JVM Integer存储为byte和short?

时间:2015-04-26 08:32:51

标签: java jvm

以下是一段代码

public class Classifier {
    public static void main(String[] args) 
    {
        Integer x = -127;//this uses bipush
        Integer y = 127;//this use bipush
        Integer z= -129;//this use sipush
        Integer p=32767;//maximum range of short still sipush
        Integer a = 128; // use sipush
        Integer b = 129786;// invokes virtual method to get Integer class

    }

}

这是

的部分字节代码
      stack=1, locals=7, args_size=1
         0: bipush        -127
         2: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
         5: astore_1
         6: bipush        127
         8: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        11: astore_2
        12: sipush        -129
        15: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        18: astore_3
        19: sipush        32767
        22: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        25: astore        4
        27: sipush        128
        30: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        33: astore        5
        35: ldc           #22                 // int 129786
        37: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        40: astore        6
        42: return

正如我在-128 to 127之间看到的整数范围,它使用bipush将一个字节作为整数值推送到堆栈。 在-32768 to 32767范围内,它使用short作为包装类sipush。接下来它使用Integer。 JVM使用byte和short来存储Integer值?

3 个答案:

答案 0 :(得分:6)

它不会在运行时存储为byteshort,只是在字节码中。 假设您想将值120存储到Integer中。您编写编译器,因此您解析源代码,并且您知道常量值120可以适合一个带符号的字节。因为您不希望在字节码中浪费空间来将值120存储为32位(4字节)值,如果它可以存储为8位(1字节),您将创建特殊指令,即可从方法字节码只加载一个byte并将其作为32位integer存储在堆栈中。这意味着,在运行时,您确实拥有integer数据类型。

生成的代码比在任何地方使用ldc更小更快,由于使用运行时常量池进行操作,因此需要与jvm进行更多交互。

bipush有2个字节,一个字节操作码,第二个字节立即constat值。因为值只有一个字节,所以它可以用于-128到127之间的值。

sipush有3个字节,一个字节操作码,第二个和第三个字节立即数  恒定价值。

bipush格式:

bipush
byte

sipush格式:

sipush
byte1
byte2

答案 1 :(得分:1)

据我所知,

正如您可以从剩余的字节代码指令那样,它不会将int存储为byteshort 首先,为什么bipushshortbipush有2个字节用于操作码,第二个用于值。即范围在-128 tp 127之间(即2次幂8) 它节省了执行的空间和时间。从remianing代码中可以看出,编译器确实将该变量的引用创建为整数类型

2: invokestatic  #16                 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;

然后astore_1 store what's on top of the stack i.e a reference here into local variable 1 类似于sipush,您可以存储范围(-32768 to 32767)中的值,因为它的3字节指令集,操作码的一个字节和 为值保留两个字节(即可以容纳2个幂16)

现在为什么不lDC JVM具有每类型常量池。字节代码需要数据,但大部分时间都需要数据 此数据太大,无法直接存储在字节代码中。 所以它存储在常量池中,字节码包含对常量池的引用。 ldc将常量池(String,int或float)中的常量#index推送到堆栈上 这会消耗额外的时间和周期 以下是ldc操作与bipush操作

之间的粗略比较

enter image description here enter image description here

JVM Bytecode ref 在这里说

  

在可能的情况下,使用bipush,sipush或者更高效   其中一个const指令而不是ldc。

答案 2 :(得分:0)

其中一个原因可能是其他答案中提到的字节代码的优点。

但是,也可以从语言开始解释这个问题。特别是,当(常量)值在目标类型中实际可表示时,您不想插入强制转换。

因此,观察到的行为的一个原因是:编译器使用可以表示给定常量值的最小可能类型

对于int(或Integer)的分配,没有必要 - 但是当字节码分配更小的"时,它不会造成任何伤害。输入"更大"类型。相反,对于较小的类型,必须使用较小的类型,因此使用"字节码表示最小的类型"是默认行为。

这也被隐含地称为"编译时缩小常量表达式"在Java语言规范的Section 5.2., Assignment Contexts中:

  

如果变量的类型是byte,short或char,则可以使用缩小的基元转换,并且常量表达式的值可以在变量的类型中表示。

     

...

     

常量表达式的编译时缩小意味着代码如:

byte theAnswer = 42;
     

是允许的。如果没有缩小,整数文字42的类型为int的事实意味着需要转换为字节:

byte theAnswer = (byte)42;  // cast is permitted but not required