javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
public class TestDate {
public static void main(String[] args) {
int i = 2;
int j = 3;
}
}
通过javap 反解析字节码文件得到下面结果:
Code:
0: iconst_2 //把2放到栈顶
1: istore_1 //把栈顶的值放到局部变量1中,即i中
2: iconst_3 //把3放到栈顶
3: istore_2 //把栈顶的值放到局部变量1中,即j中
4: return
对于 int i = 2;首先它会在栈中创建一个变量为i的引用,然后查找有没有字面值为2的地址,没找到,就开辟一个存放2这个字面值的地址,然后将i指向2的地址
请查看 JVM字节码指令表
JVM虚拟机字节码指令表_android的专栏-CSDN博客https://blog.csdn.net/u013620306/article/details/122961698
操作数栈(Operand Stack)也常称为操作栈,它是一个后入先出栈(LIFO)。同局部变量表一样,操作数栈的最大深度也在编译的时候写入到方法的Code属性的max_stacks数据项中
当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程。
这里引申出两个问题
1:怎么查看局部变量表
2:操作数栈 ---局部变量表 --PC寄存器工作过程
第二个问题研究:
我们先写一个程序 :
public class OperandStackTest {
public static void main(String[] args) {
byte i = 15;
int j = 8;
int k = i + j;
}
}
经过 javap -c 反编译之后得到如下结果
1.首先pc寄存器存的指令地址为0,所以执行bipush操作指令,将byte类型的15转为int类型压入操作数栈。
2.然后pc寄存器的指令地址变为2,则执行istore_1操作指令,意思是将上面的15存在局部变量表的下标为1的位置。有人可能会问,为什么局部变量表会从1开始,而不是从0开始。
3.pc寄存器指令地址为3,执行操作指令bipush,将8再压入栈中。
4.pc寄存器指令地址5,执行操作指令istore_2,将栈顶的8存入局部变量表2的位置
5.pc寄存器指令地址为3,执行操作指令bipush,将8再压入栈中。
6.pc寄存器指令地址5,执行操作指令istore_2,将栈顶的8存入局部变量表2的位置
7.pc寄存器指令地址为8,执行操作指令iadd,意思就是将操作数栈中的两个数进行出栈相加后再压栈,也就是8+15=23,将23再压栈,如上图。(这里的相加是通过执行引擎,最终在cpu相加,具体看后面文章,这边先不细说)
8.pc寄存器指令地址为9,执行操作指令istore_3,意思就是将操作数栈顶元素23存入局部变量表中位置为3的地方。
9. 最终一步return 返回