【JVM】while程序的字节码分析

while程序的字节码分析

首先来看两个最简单的while程序,不包含任何变量:

  • 程序1:
public class Test {
    public static void main(String[] args) {
        while(true) {
        }
    }
}

字节码指令:

public class com.wangyao2221.jvm.test.Test {
  public com.wangyao2221.jvm.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: goto          0
}

可以看到字节码的main方法中只有一个0: goto 0,没有选择分支,说明Java虚拟机节过程中已经知道这是一个特殊的循环,也就是死循环,这条字节码指令表示的就是自身跳转到自身。

  • 程序2:
public class Test {
    public static void main(String[] args) {
        while(false) {
        }
    }
}

该程序成功编译,原因是while的语句块不可达,报错内容如下:

.\Test.java:5: 错误: 无法访问的语句
        while(false) {
                     ^
1 个错误

while(false) {}无法成功编译,于是想看看if(false) {}能否成功编译,如程序3所示:

  • 程序3:
public class Test {
    public static void main(String[] args) {
        if(false) {
        }
        // 不妨也试试if(true) {}
        if(true) {
        }
    }
}

可以成功编译且字节码如下:

public class com.wangyao2221.jvm.test.Test {
  public com.wangyao2221.jvm.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: return
}

可以看到字节码指令的main方法直接就是一个return,说明Java虚拟机解析过程中已经知道两个if语句没有任何动作需要执行,所以字节码直接就丢弃了这几行多余的代码。为了验证这个想法,此时我们往两个if语句中加入一条简单的语句,如程序4

  • 程序4:
public class Test {
    public static void main(String[] args) {
        if(false) {
            int a = 0;
        }

        if(true) {
            int b = 0;
        }
    }
}

字节码指令如下:

public class com.wangyao2221.jvm.test.Test {
  public com.wangyao2221.jvm.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0 	                        // 将常数0压入操作数栈中
       1: istore_1                          // 弹出操作数栈栈顶元素,存到局部变量表第1个位置
       2: return
}

可以看到字节码指令中比程序3多出了0: iconst_0、 1: istore_1,说明if(false){}还是直接被丢弃了,而if(true){int b = 0;}则被解析成多出来的两行字节码指令——定义变量b并初始化为0

补充几条指令的区别(iconst、bipush、iconst_m1、sipush、ldc):
取值-1~5采用iconst指令  
取值-128~127采用bipush指令
取值-1时,虚拟机使用iconst_m1指令
取值-32768~32767采用sipush指令
取值-2147483648~2147483647采用 ldc 指令

上面程序3、程序4都是有点跑题,主要是由while引出的两个疑问,下面回到while程序上,让while程序复杂一点来观察字节码,首先看程序5

  • 程序5:
public class Test {
    public static void main(String[] args) {
        int n = 5;
        while (n-- > 0) {
            int a = 0;
        }
    }
}

这段代码中增加了一个变量,作为循环的条件,在循环过程中自减,字节码指令如下:

public class com.wangyao2221.jvm.test.Test {
  public com.wangyao2221.jvm.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5                          // 将常量5压入操作数栈中
       1: istore_1                          // 弹出操作数栈栈顶元素,存到局部变量表第1个位置
       2: iload_1                           // while在此处开始,取出局部变量表第1个位置的值(n),压入操作数栈
       3: iinc          1, -1               // 对局部变量表的第1个位置的值进行减1操作
       6: ifle          14                  // 将操作数栈顶的的元素(n)与0作比较(似乎没有出现0?看下面解释),如果小于等于0则跳转到底14行return处,如果这行不成立则按顺序到接下来的第9行
       9: iconst_0                          // 指令9 10是while花括号内的代码,此行是将常量0压入操作数栈中
      10: istore_2                          // 弹出操作数栈栈顶元素,存到局部变量表第2个位置
      11: goto          2                   // 跳转到2,继续下一次循环
      14: return
}

可以看到,如果多加了一个循环遍历,字节码指令就多了很多行,大部分解释已经在字节码中进行了注释,需要补充的是ifle这条指令,if顾名思义就是判断的意思,而le表示的less equal(小于等于),但是此处并没有出现0,为什么是和0比较呢?因为ifle在字节码指令中就是表示和0比较,和其他数值比较有其他的指令,严格来说ifle属于跳转指令,即如果条件成立则跳转到指定行,关于JVM其他if跳转指令如下所示:

指令.
IFEQ… , ijump if i == 0
IFNE… , ijump if i != 0
IFLT… , ijump if i < 0
IFGE… , ijump if i >= 0
IFGT… , ijump if i > 0
IFLE… , ijump if i <= 0
IF_ICMPEQ… , i , jjump if i == j
IF_ICMPNE… , i , jjump if i != j
IF_ICMPLT… , i , jjump if i < j
IF_ICMPGE… , i , jjump if i >= j
IF_ICMPGT… , i , jjump if i > j
IF_ICMPLE… , i , jjump if i <= j
IF_ACMPEQ… , o , pjump if o == p
IF_ACMPNE… , o , pjump if o != p
IFNULL… , ojump if o == null
IFNONNULL… , ojump if o != null

程序5中循环条件是n-- > 0,那如果是--n > 0字节码会是怎样的呢?我们知道n–是先取值再自减,–n则是先自减再取值,所以字节码必定是有所不同的,继续看程序6及其字节码

  • 程序6:
public class Test {
    public static void main(String[] args) {
        int n = 5;
        while (--n > 0) { //n--改为--n
            int a = 0;   
        }
    }
}

对应字节码指令如下:

public class com.wangyao2221.jvm.test.Test {
  public com.wangyao2221.jvm.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5
       1: istore_1
       2: iinc          1, -1               // 对局部变量表的第1个位置的值进行减1操作
       5: iload_1                           // while在此处开始,取出局部变量表第1个位置的值(n),压入操作数栈
       6: ifle          14
       9: iconst_0
      10: istore_2
      11: goto          2
      14: return
}

字节码指令除iinc和iload_1外其他部分与程序5是一样的,此处只给出这两行字节码指令的注释,可以发现这两行与程序5的顺序反了一下,这就是n–和--n在字节码层面的原理,这里稍作解释:①iinc 1,-1是对局部变量表第一个位置的值进行减1,②iload_1是取的是局部变量表第1个位置的值到操作数栈中,③然后ifle是从操作数栈中取出栈顶元素来使用,①是自减操作,②③就是一个取值操作,所以①和②交换顺序就体现出了n–和--n的区别

以上是本人对while循环字节码做的一些实验和分析,如果有什么问题希望帮忙指证

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WGeeker

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

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

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

打赏作者

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

抵扣说明:

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

余额充值