环境:jdk1.8



 



首先来一张图看看jvm的运行时数据区如下(有个印象即可):



 



Java虚拟机站之操作数栈_java

 



 



什么是操作数栈?



操作数栈可理解为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  返回结果



 



参考文档:



https://zm12.sm-tc.cn/?src=l4uLj8XQ0IiIiNGckZ2TkJiM0ZyQktCMl5aFl5aGltCejYuWnJOajNDIx8rIyMnL0ZeLkpM%3D&uid=480d9fa306c123a43152322dff320668&hid=2e029aff58912d22f723b33eae64e413&pos=2&cid=9&time=1545695874947&from=click&restype=1&pagetype=0020004002000408&bu=news_natural&query=%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88&mode=&v=1&force=true&wap=false&uc_param_str=dnntnwvepffrgibijbprsvdsdichei 



https://denverj.iteye.com/blog/1218359



https://iamjohnnyzhuang.github.io/java/2016/07/12/Java%E5%A0%86%E5%92%8C%E6%A0%88%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F.html



http://www.importnew.com/26842.html



https://wangwengcn.iteye.com/blog/1622195



6   (int数值范围对应的指令)



7   (反编译出来的指令)