jvm-从字节码的执行理解操作数栈

本文通过具体示例详细解析了Java字节码的执行过程,包括new指令、局部变量表等概念,展示了main方法及自定义方法的字节码执行流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  使用以下代码为例,来查看它的字节码文件。这里使用了idea的jclasslib Bytecode Viewer插件更直观的查看字节码。

public class Test {

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.add(2L,3));
    }
    
    private long add(long a,int b) {
        return (a+b);
    }
}

  首先来main方法开始执行,虚拟机栈中main方法的栈帧入栈。在这里插入图片描述
  再看到main方法的字节码和局部变量表
在这里插入图片描述
在这里插入图片描述

1、new #2:#2为常量池中的引用,该常量又引用了#33,最终为一个字符串常量,包名/类名。在这里插入图片描述
在这里插入图片描述
new指令的作用,可以从官网查看到https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.new(后续其他指令就不截图了,可以前去官网查看),在堆上创建对象并将对象的引用压入当前栈帧的操作数栈中。实际上new指令还有另外两个指令indexbyte1,indexbyte2,所以new指令占用了3行(0,1,2),dup指令只能从3开始。
在这里插入图片描述
在这里插入图片描述
2、dup:该指令会复制操作数栈栈顶的值,然后将该值入栈。
在这里插入图片描述
3、invokespecial #3 :#3为Test类的init方法,(就是自动生成的无参构造方法)。调用该方法,弹出操作数栈的objectRef,创建init方法的栈帧,将对象引用赋值给init局部变量表序号0的this对象。
在这里插入图片描述
在这里插入图片描述
init方法的局部变量表
在这里插入图片描述
看到init方法的字节码:实际上啥都没干,如果我们自己写构造方法添加逻辑,这里就会有我们自己的逻辑对应的字节码。(这里啥都没干,指的是我们没有写的业务代码,实际上init会对类的非静态属性和非静态语句块进行初始化)
在这里插入图片描述
aload_0:加载局部变量表位置为0的值,到操作数栈。
invokespecial #1 :然后调用父类object类的init方法。
return:丢弃当前操作数栈的所有数据,将控制权返还给调用方,恢复调用方的栈桢。
此时init方法执行完毕。
在这里插入图片描述
看回main方法字节码的执行流程
4、astore_1:将操作数栈栈顶的数据弹出,存入到局部变量表的序号为1的位置,此时main方法中的test局部变量就有了值。此时操作数栈啥都没有了。
在这里插入图片描述
5、getstatic #4 <java/lang/System.out> : 获取到Sysytem的静态对象out,并且压入操作数栈。
在这里插入图片描述
6、aload_1:加载局部变量表位置为1的值压入操作数栈

在这里插入图片描述

7、ldc2_w #5 <2> :从常量池加载long或者double类型(64位)的数据压入操作数,由于2被声明为long类型,在常量池表中占据两个位置。

在这里插入图片描述

在这里插入图片描述
8、iconst_3:将int类型的数据(值为3)压入操作数栈
在这里插入图片描述
9、invokespecial #7 <jvm/Test.add>:调用Test的add方法。

首先看到add方法的局部变量表
在这里插入图片描述
然后将3,2,objectRef弹出。创建add方法的栈帧,压入当前虚拟机栈中。弹出的值赋值到add方法的栈帧的局部变量表,this对象(0号位置)为objectRef的值,2为1号位置的值,3为3号位置的值。由于a对象是long类型,占用2个位置
在这里插入图片描述
在这里插入图片描述

此时看到add方法的字节码
10、lload_1:加载局部变量表位置1的long类型的数据压入操作数栈。(2)操作数栈中的值
   iload_1:加载局部变量表位置3的int类型的数据压入操作数栈。(3,2)
   i2l:将操作数栈顶的int数据弹出,转化为long类型的数据,再压入栈中。(我们栈顶的int类型的3被转化为了long类型的3) (3,2)
   ladd:弹出栈顶两个long类型的数据,进行相加,相加的结果再压入栈中。(5)
   lreturn:将操作数栈栈顶的long类型的数据弹出,压入调用方(main方法)栈桢的操作数栈中,丢弃当前操作数栈的所有数据,将控制权返还给调用方,恢复调用方的栈桢
在这里插入图片描述
此时add方法执行完毕
在这里插入图片描述
在这里插入图片描述
看回main方法的执行
11、 invokevirtual #8 <java/io/PrintStream.println>:调用println方法(println方法就不再画图了和看了),将操作数栈的参数和对象引用弹出,创建println方法的栈帧,然后局部变量表存入对象引用和参数值,println方法没有返回值,最后执行return指令返回。
12、return。main方法执行完毕。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值