环境:jdk1.8
首先来一张图看看jvm的运行时数据区如下(有个印象即可):
什么是操作数栈?
操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区。
- 存储的数据与局部变量表一致含int、long、float、double、reference、returnType,操作数栈中byte、short、char压栈前(bipush)会被转为int。
- 数据运算的地方,大多数指令都在操作数栈弹栈运算,然后结果压栈。
- java虚拟机栈是方法调用和执行的空间,每个方法会封装成一个栈帧压入占中。其中里面的操作数栈用于进行运算,当前线程只有当前执行的方法才会在操作数栈中调用指令(可见java虚拟机栈的指令主要取于操作数栈)。
-1~5、-128~127、-32768~32767、-2147483648~2147483647范围分别对应的指令是iconst、bipush、sipush、ldc(这个就直接存在常量池了)
举例一个加法过程:
public class Hello {
public static void main(String[] args) {
Hello hello = new Hello();
int testVal = hello.test();
}
public int test() {
//int类型
int a = 5 + 10; //验证直接相加在编译阶段已合并完结果
//探究变量与常量的相加过程
//验证int不在-1~5,在-128~127范围的指令是bipush
//验证int不在-128~127,在-32768~32767范围的指令是sipush
//验证int不在-32768~32767,在-2147483648~2147483647范围的指令是ldc(ldc:从常量池取并压栈,所以这个范围的int是存在常量池)
//验证byte、short、char在操作数栈压栈前均会转为int
short a_s = 5 + 10;
short b_s = (short)(a_s + 3);
//以下验证float、double、String均是从常量池中取出(均使用了ldc、ldc_w、ldc2_w其中一个)
float a_f = 5.00F + 10.00F;
float b_f = a_f + 3.00F;
//double类型
double a_d = 5.00D + 10.00D;
double b_d = a_d + 3.00D;
//String类型
String a_str = "a" + "b";
String b_str = a_str + "c";
return b;
}
}
首先这个会保存在方法的Code 属性中,javac Hello.java编译,然后javap -verbose Hello.class反编译分析test()方法如下:
Code:
stack=4, locals=13, args_size=1
//1 15压入操作数的栈顶(编译过程中5+10合并成15,并且由于15在-128-127范围,即用bipush) 压栈
//2 从栈顶弹出并压入局部变量表访问索引为1的Slot 弹栈入局部变量表
//3 将局部变量表中访问索引为1的Slot重新压入栈顶 局部变量表入栈
//4 数值3压入操作数的栈顶(范围-1~5,即用指令iconst) 压栈
//5 将栈顶的前两个弹出并进行加法运算后将结果重新压入栈顶 前两弹栈相加
//6 从栈顶弹出并压入局部变量表访问索引为2的Slot 弹栈入局部变量表
//7 将局部变量表中访问索引为2的Slot重新压入栈顶 局部变量表入栈
8: bipush 6 //8 6压入操作数的栈顶(在-128-127范围,用bipush指令)
10: iadd
11: istore_2
12: iload_2
//9 128压入操作数的栈顶(在-32768~32767范围,用sipush指令)
16: iadd
17: istore_2
18: iload_2
//10 128压入操作数的栈顶(在-2147483648~2147483647范围,用ldc指令)
21: iadd
22: istore_2
//11 验证了short、byte、char压栈前都会转为int
25: istore_3
26: iload_3
27: iconst_3
28: iadd
29: i2s
30: istore 4
//12 以下验证float、double、String均是从常量池中取出(均使用了ldc、ldc_w、ldc2_w其中一个)
34: fstore 5
36: fload 5
38: ldc #7 // float 3.0f
40: fadd
41: fstore 6
43: ldc2_w #8 // double 15.0d
46: dstore 7
48: dload 7
50: ldc2_w #10 // double 3.0d
53: dadd
54: dstore 9
56: ldc #12 // String ab
58: astore 11
60: new #13 // class java/lang/StringBuilder
63: dup
64: invokespecial #14 // Method java/lang/StringBuilder."<init>":()V
67: aload 11
69: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
72: ldc #16 // String c
74: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
77: invokevirtual #17 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
80: astore 12
82: iload_2
//13 返回结果
参考文档:
2 https://denverj.iteye.com/blog/1218359
4 http://www.importnew.com/26842.html
5 https://wangwengcn.iteye.com/blog/1622195
6 (int数值范围对应的指令)
7 (反编译出来的指令)