• JVM学习-字节码指令集(三)


    代码下载

    操作数栈管理指令
    • 如同操作一个普通数据结构中的堆栈那样,JVM提供的操作数栈管理指令,可以用于直接操作数栈的指令
    • 将一个或两个元素从栈顶弹出,并且直接废弃:pop,pop2
    • 复制栈顶一个或两个数值并将复制值成双份的复制值重新压入栈顶:dup,dup2,dup_x1,dup_x1,dup_x2,dup_x2
    • 将栈最顶端的两个Slot数值位置交换,swap,Java虚拟机没有提供交换两个64位数据类型(long,double)数值的指令
    • 指令nop,是一个非常特殊指令,它的字节码为0x00。和汇编语言中的nop一样,表示什么都不做,这条指令一般可用于调试,占位等
    • 上述指令属于通用型,对栈的压入或弹出无需指明数据类型
    • 说明
    • 不带_x指令是复制栈顶数据并压入栈顶,包括两个指令,dup和dup2,dup的系数代表要复制的Slot个数
    • dup开头的指令用于复制1个Slot的数据,例如1个int可1个reference类型数据
    • dup2开头的指令用于复制2个Slot的数据,如1个long,2个int或1个int+1个float类型数据
    • 带_x的指令是复制栈顶数据并插入栈顶以下的某个位置,共有4个指令,dup_x1,dup_x1,dup_x2,dup_x2,对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置,因此
    • dup_x1插入位置:1 + 1 = 2,即栈顶2个Slot下面
    • dup_x2插入位置:1 + 2 = 3,即栈顶3个Slot下面
    • dup2_x1插入位置:2 + 1 = 3,即栈顶3个Slot下面
    • dup2_x2插入位置:2 + 2 = 4,即栈顶4个Slot下面
    • pop:将栈顶的1个Slot数值出栈,如1个short类型数值
    • pop2:将栈顶的2个Slot数值出栈,如1个double类型数值,或2个int类型数值
    //可以编译后,通过IDEA的jclasslib插件查看字节码
    public class StackOperateTest {
        public void print() {
            Object obj = new Object();
            //String info = obj.toString();
            obj.toString();
        }
        public void foo() {
            bar();
        }
        public long bar() {
            return 0;
        }
        public long nextIndex() {
            return index ++;
        }
        private long index = 0;
    }
    
    
    控制转移指令
    比较指令
    • 比较指令的作用是比较栈顶两个元素的大小,并将比较结果入栈
    • 比较指令有dcmpg,dcmpl,fcmpg,fcmpl,lcmp
    • 对于double和float类型的数字,由于NaN的存在,各有两个版本的比较指令,以float为例,fcmpg和fcmpl两个指令,它们的区别在于在数字比较时,若遇到NaN值,处理结果不同
    • 指令dcmpl和dcmpg也是类似的,根据其命名可以推测其含义
    • 指令lcmp针对long型整数,由于long整数没有NaN值,无需准备两套指令
    • 举例:指令fcmpg和fcmpl都从栈中弹出两个操作数,并将它们做比较,设栈顶的元素为v2,栈顶顺位第2位的元素为v1,若v1=v2,则压入0,若v1>v2则压入1,若v1
    条件跳转指令
    • 条件跳转指令通常和比较指令结合使用,在条件跳转执行前,一般可以先用比较指令进行栈顶元素的准备,然后进行条件跳转

    • 条件跳转指令:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,这些指令都接收两个字节的操作数,用于跳转的位置(16位符号整数作为当前位置的offset)

    • 它们统一含义为:弹出栈顶元素,测试它是否满足某一条件,如果满足条件,则跳转到给定位置
      在这里插入图片描述

    • 注:

    • 对于boolan,byte,char,short类型的条件分支比较操作,都使用int类型比较指令完成
    • 对于long,float,double类型的条件分支比较操作,则会执行相应类型的比较运算指令,运算指令会返回一个整数值到操作数栈中,随后再执行int类型的条件分支比较操作来完成整个分支跳转
    • 由于各种类型的比较最终会转为int类型的比较操作,所以JVM提供的int类型的条件分支指令是最为丰富和强大的
    比较条件跳转指令
    • 比较条件跳转指令类似于比较指令和条件跳转指令的结合体,它将比较和跳转两个步骤合二为一
    • 这类指令有if_icmpeq,if_icmpne,if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq和if_acmpne
    • 其中指令助词符加上"if_"后,以字符i开头的指令针对int型整数操作(包括short和byte类型),以字符a开头的指令表示对象引用的比较
      在这里插入图片描述
    • 这些指令都接收两个字节的操作数作为参数,用于计算跳转的位置,同时在执行指令时,栈顶需要准备两个元素进行比较,指令执行完成后,栈顶的这两个元素被清空,且没有任何数据入栈,如果预设条件成立,则执行跳转,否则,继续执行下一条语句
    //可通过jclasslib插件查看字节码
     public void ifcompare1() {
            int i = 10;
            int j = 20;
            System.out.println(i < j);
        }
        public void ifcompare2() {
            short s1 = 9;
            byte b1 = 20;
            System.out.println(s1 > b1);
        }
        public void ifcompare3() {
            Object obj1 = new Object();
            Object obj2 = new Object();
            System.out.println(obj1 == obj2);
            System.out.println(obj1 != obj2);
        }
        //ifcompare3字节码
         0 new #10 <java/lang/Object>
     3 dup
     4 invokespecial #1 <java/lang/Object.<init>>
     7 astore_1
     8 new #10 <java/lang/Object>
    11 dup
    12 invokespecial #1 <java/lang/Object.<init>>
    15 astore_2
    16 getstatic #4 <java/lang/System.out>
    19 aload_1
    20 aload_2
    21 if_acmpne 28 (+7)
    24 iconst_1
    25 goto 29 (+4)
    28 iconst_0
    29 invokevirtual #5 <java/io/PrintStream.println>
    32 getstatic #4 <java/lang/System.out>
    35 aload_1
    36 aload_2
    37 if_acmpeq 44 (+7)    //比较相等时跳转,见上图
    40 iconst_1
    41 goto 45 (+4)
    44 iconst_0
    45 invokevirtual #5 <java/io/PrintStream.println>
    48 return
    
    多条件分支跳转指令
    • 多条件分支跳转指令是专门为switch-case语句设计的,主要有tableswitch和lookupswitch
      在这里插入图片描述
    • tableswitch要求多个条件分支值是连续的,它内部只存放起始值和终止值,以及若干个跳转偏移量,通过给定的操作数index,可以立即定位到跳转偏移量位置,因此效率较高
    • lookupswitch内部存放着各个离散的case-offset对,每次执行都要搜索全部的case-offset对,找到匹配的case值,并根据对应的offset计算跳转地址,因此效率较低
    • 指令tableswitch的case是连续的,只需要记录最低值和最高值,以及每一个项对应的offset偏移量,根据给定index值通过简单计算可直接定位offset
    public void switch1(int select) {
            int num;
            switch (select) {
                case 1:
                    num = 10;
                    break;
                case 2:
                    num = 20;
                    break;
                case 3:
                    num = 30;
                    break;
                default:
                    num = 40;
            }
        }
    //--字节码
     0 iload_1
     1 tableswitch 1 to 3	1:  28 (+27)    //switch... case 1-3
    	2:  34 (+33)
    	3:  40 (+39)
    	default:  46 (+45)
    28 bipush 10
    30 istore_2
    31 goto 49 (+18)
    34 bipush 20
    36 istore_2
    37 goto 49 (+12)
    40 bipush 30
    42 istore_2
    43 goto 49 (+6)
    46 bipush 40
    48 istore_2
    49 return
    
    public void switch2(int select) {
            int num;
            switch (select) {
                case 100:
                    num = 10;
                    break;
                case 500:
                    num = 20;
                    break;
                case 200:
                    num = 30;
                    break;
                default:
                    num = 40;
            }
        }
        
    //字节码
     0 iload_1
     1 lookupswitch 3
    	100:  36 (+35)
    	200:  48 (+47)
    	500:  42 (+41)
    	default:  54 (+53)
    36 bipush 10
    38 istore_2
    39 goto 57 (+18)
    42 bipush 20
    44 istore_2
    45 goto 57 (+12)
    48 bipush 30
    50 istore_2
    51 goto 57 (+6)
    54 bipush 40
    56 istore_2
    57 return
    
    无条件跳转指令
    • 无条件跳转指令为goto,指令goto接收两个字节的操作数,共同组成一个带符号的整数,用于指定指令的偏移量,指令执行的目的就是跳转到偏移量给定的位置处
    • 如果指令偏移量太大,超过双字节的带符号整数的范围,则可以使用指令goto_w,它和goto有相同的作用,接收4个字节的操作数,可以表示更大的地址范围
    • 批评jsr,jsr_w,ret虽也是无条件跳转,但主要用于try-finally语句,且已经被虚拟机逐渐废弃,故不在这里介绍
      在这里插入图片描述
  • 相关阅读:
    测试平台项目部署一(手动部署)
    列表初始化与右值引用
    vue相关面试题:diff算法
    画一个 “月饼” 陪我过中秋,使用 ESP32-C3 制作炫彩月饼(我为嵌入式工程师争取月饼)
    springboot+vue+elementUI企业客户信息反馈平台#毕业设计
    ARM——cortex-A7核 按键中断实验
    梯度下降法公式推导+实战--以一元、多元线性回归为例-猛男技术控
    VUE3,AXIOS
    MyBatis Mapper映射处理CLOB和BLOB类型
    26-Java访问者模式 ( Visitor Pattern )
  • 原文地址:https://blog.csdn.net/xuwenpeng/article/details/139311594