原始java代码
package cn.itcast.jvm.t3.bytecode;
/**
* 演示 字节码指令 和 操作数栈、常量池的关系
*/
public class Demo3_1 {
public static void main(String[] args) {
int a = 10;
int b = Short.MAX_VALUE + 1;
int c = a + b;
System.out.println(c);
}
}
常量池载入运行时常量池
由类加载器把main方法所在的类做类加载的操作,将class文件中的常量池的消息载入到运行时常量池中(运行时常量池属于方法区内的特殊区域)
方法字节码载入方法区
main线程开始运行,分配栈帧内存
局部变量表 操作数栈
bipush 10
- 将一个 byte 压入操作数栈(其长度会补齐 4 个字节),类似的指令还有
- sipush 将一个 short 压入操作数栈(其长度会补齐 4 个字节)
- ldc 将一个 int 压入操作数栈
- ldc2_w 将一个 long 压入操作数栈(分两次压入,因为 long 是 8 个字节)
- 这里小的数字都是和字节码指令存在一起,超过 short 范围的数字存入了常量池
istore_1
将操作数栈顶数据弹出,存入局部变量表的slot1
ldc #3
- 从常量池加载 #3 数据到操作数栈
- 注意 Short.MAX_VALUE 是 32767,所以 32768 = Short.MAX_VALUE + 1 实际是在编译期间计算好的
istore_2
执行a+b要在操作数栈中进行,执行引擎将局部变量表中a,b读取至操作数栈中
iload_1
iload_2
iadd
将俩弹出操作数栈,并将结果存入操作数栈
getstatic #4
从运行时常量池中的引用找到存在堆中的对象
iload_3
invokevirtual #5
- 找到常量池 #5 项
- 定位到方法区 java/io/PrintStream.println:(I) V 方法
- 生成新的栈帧(分配 locals、stack 等)
- 传递参数,执行新栈帧中的字节码
- 执行完毕,弹出栈帧
- 清除 main 操作数栈内容
return
- 完成 main 方法调用,弹出 main 栈帧
- 程序结束
从字节码角度分析 a++
package cn.itcast.jvm.t3.bytecode;
/**
* 从字节码角度分析 a++ 相关题目
*/
public class Demo3_2 {
public static void main(String[] args) {
int a = 10;
int b = a++ + ++a + a--;
System.out.println(a);
System.out.println(b);
}
}
- 注意 iinc 指令是直接在局部变量 slot 上进行运算
- a++ 和 ++a 的区别是先执行 iload 还是 先执行 iinc