以下是一段代码
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值?
答案 0 :(得分:6)
它不会在运行时存储为byte
或short
,只是在字节码中。
假设您想将值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
存储为byte
或short
首先,为什么bipush
或short
:bipush
有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
操作
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