JVM 字节码指令解读

public class demo1 {
    public static void main(String[] args) {
        int num=1;
        num++;
        System.out.println(num);
    }
}

如上图所示,定义了一个简单类

打开方法,发现有两个方法,分别是init 和main ,其中init是object的构造函数,因为任何类(除object以外)都继承于object。所以当我们没有定义构造函数的时候,虚拟机默认我们调用object的构造函数。

我们接下来主要看看main方法:

0 iconst_1
 1 istore_1
 2 iinc 1 by 1
 5 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
 8 iload_1
 9 invokevirtual #3 <java/io/PrintStream.println : (I)V>
12 return

这是相应的操作数栈指令,iconst_1就是把int型的1push到操作数栈

iconst_m1 = 2 (0x2)    -1

iconst_0 = 3 (0x3)        0

iconst_1 = 4 (0x4)        1

iconst_2 = 5 (0x5)        2

iconst_3 = 6 (0x6)        3

iconst_4 = 7 (0x7)        4

iconst_5 = 8 (0x8)        5

可以看到-1 ---5的指令,不在这个范围类我们用其他指令,总结如下:

 istore_1:将栈顶的元素pop,并把它存到局部变量表索引为1的地方

iinc 1 by 1:直接将局部变量表中索引为1的元素加1,这里num就变成2了

后面是加载静态方法,并把1入栈,后面调用方法。

接下来看看if--else怎么执行指令:

public class demo1 {
    public int num=1;
    public  void judge(){
        if(num==1){
            num=2;
        }
        else num=3;
    }
}

这是一个简单的if--else判断语句,逻辑很简单,然后我们看看相对应的字节码:

0 aload_0
 1 getfield #2 <demo1.num : I>
 4 iconst_1
 5 if_icmpne 16 (+11)
 8 aload_0
 9 iconst_2
10 putfield #2 <demo1.num : I>
13 goto 21 (+8)
16 aload_0
17 iconst_3
18 putfield #2 <demo1.num : I>
21 return

局部变量表只有索引为0的this指针。

0:读入this指针         1:通过getfield将this指针所指向的对象的属性num压入栈,也就是将1压入栈        4:将常量1压入栈        5:将栈顶的两个元素进行比较 如果它们两不相等,转到16号指令

8:读入this指针        9:常量2入栈        10:给this指针的属性num赋值2      13:转到21行结束

 16:读入this指针        17:常量3入栈        18:给this指针的属性num赋值3        21:结束

此外,下面展示同步代码块是怎么执行的:

public class demo1 {
    private int i=0;
    private Object obj =new Object();
    public  void test01(){
        synchronized (obj){
            i--;
        }
    }

}

此处我是再test01方法里面定义了一个同步代码块

0 aload_0
 1 getfield #4 <demo1.obj : Ljava/lang/Object;>
 4 dup
 5 astore_1
 6 monitorenter
 7 aload_0
 8 dup
 9 getfield #2 <demo1.i : I>
12 iconst_1
13 isub
14 putfield #2 <demo1.i : I>
17 aload_1
18 monitorexit
19 goto 27 (+8)
22 astore_2
23 aload_1
24 monitorexit
25 aload_2
26 athrow
27 return

 上面也列出了异常表

字节码指令稍微有点长,我一步步解析

0:从局部遍历表0的位置加载this指针        1:通过this拿到它的obj属性,入栈,于此同时this指针出栈        4:dup赋值一份obj         5:把栈顶的obj存到局部变量表索引为1的地方 ,此时栈里面还剩下一个obj        6:请求进入同步代码块,堆中的obj对象对象头的同步锁标志发生改变,以防止其它进程同时进入        7:将this指针入栈        8:复制一份this指针        9:通过this得到i                12:常量1入栈        13:出栈两个元素并相减(i-1),并把结果入栈        14:将结果赋值给this所指对象的i属性        17:  将obj压入栈        18:请求退出,  堆中的obj对象对象头的同步锁标志发生改变,其他进程可以进入        19:跳转到27结束        22:将异常对象存到2索引的位置

23:将obj'压入栈        24:请求退出,  堆中的obj对象对象头的同步锁标志发生改变,其他进程可以进入          25:将异常对象存到2索引的位置        26:抛出异常        27:结束

结合异常表可以看到,若一个进程在进入同步代码块后发生了某些异常,那么它跳转到22号指令,将异常对象存入局部变量表,并解锁(必不可少),如果在后面执行解锁过程当中又发生异常,那么又跳转到前面,直到执行完解锁的操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这代码有点上头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值