1.操作数栈
解释时,JVM会为方法分配一个栈帧,而栈帧又由 局部变量表,操作数帧,方法引用,动态链接 组成
方法中的每条指令执行时,要求该指令的操作数已经压入栈中;执行指令时会将操作数从栈中弹出,是否将操作数再次压入栈中取决与具体的命令。
new,dup指令
使用new关键字创建对象的时候出现的字节码指令,通常伴随着 dup 指令 ,dup指令将复制一份操作数栈顶的值
这里是因为 invokespecial 调用类的构造方法时,将会消耗new的结果引用,如果我们不复制一份 ,那么这个引用就丢掉了。
public class ByteCodeDemo {
public static void main(String[] args) {
Object b = new Object();
}
}
0 new #2 <java/lang/Object>
3 dup
4 invokespecial #1 <java/lang/Object.<init> : ()V>
7 astore_1
8 return
pop 指令
public static void main(String[] args) throws InterruptedException {
ByteCodeDemo byteCodeDemo = new ByteCodeDemo();
byteCodeDemo.methodOne();
}
public String methodOne(){
return "no";
}
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
Code:
0: new #2 // class com/sz/jasyptdemo/ByteCodeDemo
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method methodOne:()Ljava/lang/String;
12: pop
13: return
调用了methodOne()方法,这个方法是有返回结果的,但是我们并没有接受 因此JVM会调用 pop指令,将返回值抛弃掉
iconst,bipush,sipush,ldc
iconst 表示加载一个常量,常量的值范围在 -1 ~5 之间,bipush 加载一个字节所能表示的int值,sipush加载两个字节所能表示的int值,ldc 则能加载任意值
public static void main(String[] args) throws InterruptedException {
int a = 5;
int b = 6;
int c = 129;
int d = 32768;
}
0: iconst_5
1: istore_1
2: bipush 6
4: istore_2
5: sipush 129
8: istore_3
9: ldc #2 // int 32768
11: istore 4
13: return
Throwable
如果抛出异常,会将操作数栈清空,然后将异常实例压入操作数栈
2. 局部变量表 (数组)
加载与存储 load,store
Java 方法栈桢的另外一个重要组成部分则是局部变量区,字节码程序可以将计算的结果缓存在局部变量区之中。
Java 虚拟机将局部变量区当成一个数组,如果是实例方法,那么局部变量表这个数组的0号下标位置就是就是this指针,1号下标位置就是 参数,后面依次存放局部变量。
public static void main(String[] args) throws InterruptedException {
ByteCodeDemo byteCodeDemo = new ByteCodeDemo();
byteCodeDemo.method(3);
}
public void method(int i){
int a = 5;
int b = 6;
int c = 129;
int d = 32768;
}
对应的局部变量表
因为调用的是实例方法,所以本地变量表序号0的位置上是 this指针,1号上是 方法参数 i,后面依次是方法从上往下的局部变量。
JVM对局部变量的主要有两组命令 加载 命令 load, 存储命令 sotre
public static void main(String[] args) throws InterruptedException {
ByteCodeDemo byteCodeDemo = new ByteCodeDemo();
byteCodeDemo.method(3);
}
public void method(int i){
int a = 5;
int b = 6;
int c = 129;
int d = 32768;
if (d<300){
System.out.println("...");
}
}
0 iconst_5
1 istore_2
2 bipush 6
4 istore_3
5 sipush 129
8 istore 4
10 ldc #5 <32768>
12 istore 5
14 iload 5
16 sipush 300
19 if_icmpge 30 (+11)
22 getstatic #6 <java/lang/System.out : Ljava/io/PrintStream;>
25 ldc #7 <...>
27 invokevirtual #8 <java/io/PrintStream.println : (Ljava/lang/String;)V>
30 return
如上的字节码指令所示,首先是加载常量5到栈顶,然后调用 istore 将栈顶元素存储到局部变量表下标为2的位置上;然后调用bispush,将6压到栈顶… 然后调用iload指令 加载局部变量表下标为5的内容,与栈顶元素 300 比较,如果满足条件,往下执行;如果不满足,跳到偏移量为30的地方执行return返回;
不同基本数据的类型,有不同的load和store命令,如下图所示
通常对局部变量的操作是首先加载值然后压到操作数栈中进行计算,如下所示
public void method(int i){
int a = 5;
int b = a+10;
int c = 129;
int d = 32768;
if (d<300){
System.out.println("...");
}
}
0 iconst_5
1 istore_2
2 iload_2
3 bipush 10
5 iadd
6 istore_3
7 sipush 129
10 istore 4
12 ldc #5 <32768>
14 istore 5
16 iload 5
18 sipush 300
21 if_icmpge 32 (+11)
24 getstatic #6 <java/lang/System.out : Ljava/io/PrintStream;>
27 ldc #7 <...>
29 invokevirtual #8 <java/io/PrintStream.println : (Ljava/lang/String;)V>
32 return
如第2,3,5行指令所示;
但是也有指令能直接对局部变量表上的数值进行运算: 自增,自减操作
直接操作局部变量表的指令 iinc
public void method(int i){
int a = 1;
int b = 10;
a++;
b--;
}
0 iconst_1
1 istore_2
2 bipush 10
4 istore_3
5 iinc 2 by 1
8 iinc 3 by -1
11 return
如上字节码所示 iinc + 局部变量表的下标 + by + 增加的值
3. 其他指令
instanceof
后跟目标类,判断栈顶元素是否为目标类 / 接口的实例。是则压入 1,否则压入 0
public void method(int i){
Date date = new Date();
if (date instanceof Object) {
System.out.println("ofcause");
}
}
0 new #5 <java/util/Date>
3 dup
4 invokespecial #6 <java/util/Date.<init> : ()V>
7 astore_2
8 aload_2
9 instanceof #7 <java/lang/Object>
12 ifeq 23 (+11)
15 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
18 ldc #9 <ofcause>
20 invokevirtual #10 <java/io/PrintStream.println : (Ljava/lang/String;)V>
23 return
getstatic
访问静态字段,见上
monitorente monitorexit
为栈顶元素加锁和解锁
public void method(int i){
synchronized (new Object()){
System.out.println("sync");
}
}
0 new #5 <java/lang/Object>
3 dup
4 invokespecial #1 <java/lang/Object.<init> : ()V>
7 dup
8 astore_2
9 monitorenter
10 getstatic #6 <java/lang/System.out : Ljava/io/PrintStream;>
13 ldc #7 <sync>
15 invokevirtual #8 <java/io/PrintStream.println : (Ljava/lang/String;)V>
18 aload_2
19 monitorexit
20 goto 28 (+8)
23 astore_3
24 aload_2
25 monitorexit
26 aload_3
27 athrow
28 return