JVM

28-字节码指令

2021-06-27  本文已影响0人  紫荆秋雪_文

一、概述

do {
  自动计算PC寄存器的值加1;
  根据PC寄存器的指示位置,从字节码流中取出操作码;
  if(字节码存在操作数)从字节码流中取出操作数;
  执行操作码所定义的操作;
}while(字节码长度 > 0)

二、字节码与数据类型

在Java虚拟机的指令集中,大多数的指令都包含了其操作所对应的数据类型信息。如 iload 指令用于从局部变量表中加载 int 类型的数据到“操作数栈”中,而fload指令加载的则是 float 类型的数据

1、对于大部分与数据类型相关的字节码指令,它们的操作码助记符中都有特殊的字符来表名专门为哪种数据类型服务
2、指令分类
3、做值相关操作时

三、加载与存储指令

1、作用
2、常用指令
3、说明
4、操作数栈(Operand Stack)
5、局部变量表(Local Variables)
public class LoadAndStoreTest {

    public void load(int num, Object obj, long count, boolean flag, short[] arr) {
        System.out.println("num = " + num);
        System.out.println("obj = " + obj);
        System.out.println("count = " + count);
        System.out.println("flag = " + flag);
        System.out.println("arr = " + arr);
    }

}
  0 getstatic #2 <java/lang/System.out>
  3 new #3 <java/lang/StringBuilder>
  6 dup
  7 invokespecial #4 <java/lang/StringBuilder.<init>>
 10 ldc #5 <num = >
 12 invokevirtual #6 <java/lang/StringBuilder.append>
 15 iload_1
 16 invokevirtual #7 <java/lang/StringBuilder.append>
 19 invokevirtual #8 <java/lang/StringBuilder.toString>
 22 invokevirtual #9 <java/io/PrintStream.println>
 25 getstatic #2 <java/lang/System.out>
 28 new #3 <java/lang/StringBuilder>
 31 dup
 32 invokespecial #4 <java/lang/StringBuilder.<init>>
 35 ldc #10 <obj = >
 37 invokevirtual #6 <java/lang/StringBuilder.append>
 40 aload_2
 41 invokevirtual #11 <java/lang/StringBuilder.append>
 44 invokevirtual #8 <java/lang/StringBuilder.toString>
 47 invokevirtual #9 <java/io/PrintStream.println>
 50 getstatic #2 <java/lang/System.out>
 53 new #3 <java/lang/StringBuilder>
 56 dup
 57 invokespecial #4 <java/lang/StringBuilder.<init>>
 60 ldc #12 <count = >
 62 invokevirtual #6 <java/lang/StringBuilder.append>
 65 lload_3
 66 invokevirtual #13 <java/lang/StringBuilder.append>
 69 invokevirtual #8 <java/lang/StringBuilder.toString>
 72 invokevirtual #9 <java/io/PrintStream.println>
 75 getstatic #2 <java/lang/System.out>
 78 new #3 <java/lang/StringBuilder>
 81 dup
 82 invokespecial #4 <java/lang/StringBuilder.<init>>
 85 ldc #14 <flag = >
 87 invokevirtual #6 <java/lang/StringBuilder.append>
 90 iload 5
 92 invokevirtual #15 <java/lang/StringBuilder.append>
 95 invokevirtual #8 <java/lang/StringBuilder.toString>
 98 invokevirtual #9 <java/io/PrintStream.println>
101 getstatic #2 <java/lang/System.out>
104 new #3 <java/lang/StringBuilder>
107 dup
108 invokespecial #4 <java/lang/StringBuilder.<init>>
111 ldc #16 <arr = >
113 invokevirtual #6 <java/lang/StringBuilder.append>
116 aload 6
118 invokevirtual #11 <java/lang/StringBuilder.append>
121 invokevirtual #8 <java/lang/StringBuilder.toString>
124 invokevirtual #9 <java/io/PrintStream.println>
127 return

1、常量入栈指令

1、指令const系统
2、指令push系统
3、指令ldc系统
4、指令ldc_w系统
5、总结 image.png
public class LoadAndStoreTest {
    // 2、常量入栈指令
    public void pushConstLdc() {
        int i = -1;
        int a = 5;
        int b = 6;
        int c = 127;
        int d = 128;
        int e = 32767;
        int f = 32768;
    }
}
 0 iconst_m1
 1 istore_1
 2 iconst_5
 3 istore_2
 4 bipush 6
 6 istore_3
 7 bipush 127
 9 istore 4
11 sipush 128
14 istore 5
16 sipush 32767
19 istore 6
21 ldc #2 <32768>
23 istore 7
25 return

2、出栈转入局部变量表指令

四、算术指令

1、运算时的溢出:数据运算可能会导致溢出,例如两个很大的正整数相加,结果可能是一个负数。Java虚拟机规范并无明确规定过整型数据溢出的具体结果,仅规定了在处理整型数据时,只有除法指令以及求余指令中当出现除数为0时会导致虚拟机抛出异常 ArithmeticException

    @Test
    public void method1() {
        int i = 0;
        int j = i / 0;
        System.out.println(j);
    }
java.lang.ArithmeticException: / by zero
    @Test
    public void method2() {
        int i = 10;
        double j = i / 0.0;
        System.out.println(j);
    }
Infinity
    @Test
    public void method3() {
        int i = 0;
        double j = i / 0.0;
        System.out.println(j);
    }
NaN

2、运算模式

3、NaN值使用:当一个操作产生溢出时,将会使用有符号的无穷大表示,如果某个操作结果没有明确的数学定义的话,将会使用NaN值来表示。而且所有使用NaN值作为操作数的算术操作,结果都会返回 NaN

4、所有算术指令

实例

1、实例1:float 取反指令
    @Test
    public void method4() {
        float i = 10;
        float j = -i;
        i = -j;
    }
0 ldc #2 <10.0>
2 fstore_1
3 fload_1
4 fneg
5 fstore_2
6 fload_2
7 fneg
8 fstore_1
9 return
2、实例2:i = i + 10 与 i += 10;
    @Test
    public void method5() {
        int i = 100;
        i = i + 10;
    }
0 bipush 100
2 istore_1
3 iload_1
4 bipush 10
6 iadd
7 istore_1
8 return
:i += 10;
    public void method5() {
        int i = 100;
        i += 10;
    }
0 bipush 100
2 istore_1
3 iinc 1 by 10
6 return
3、关于i++ 和 ++i
3.1、分析++i
    /**
     * 关于(前)++i 和 (后)i++
     */
    public void method6() {
        int i = 10;
        ++i;
    }
0 bipush 10
2 istore_1
3 iinc 1 by 1
6 return
3.2、i++字节码分析结果与++i一样 image.png

实例4 int a = i++;

    public void method7() {
        int i = 10;
        int a = i++;
    }
0 bipush 10
2 istore_1
3 iload_1
4 iinc 1 by 1
7 istore_2
8 return
实例5:int a = ++i
    public void method8() {
        int i = 10;
        int a = ++i;
    }
0 bipush 10
2 istore_1
3 iinc 1 by 1
6 iload_1
7 istore_2
8 return

4、比较指令

五、类型转换指令

1类型转换指令说明

2、宽化类型转换(Widening Numeric Conversions)

2.1、转换规则
2.2、精度损失问题
2.3、实例测试
package com.lkty.loadandstore;

/**
 * 类型转换指令
 */
public class ClassCastTest {

    /**
     * 宽化类型转换
     */
    public void upCast1() {
        int i   =   10;
        long    l   =   i;
        float   f   =   i;
        double  d   =   i;

        float   f1  =   l;
        double  d1  =   l;

        double  d2  =   f1;
    }
}
 0 bipush 10
 2 istore_1
 3 iload_1
 4 i2l
 5 lstore_2
 6 iload_1
 7 i2f
 8 fstore 4
10 iload_1
11 i2d
12 dstore 5
14 lload_2
15 l2f
16 fstore 7
18 lload_2
19 l2d
20 dstore 8
22 fload 7
24 f2d
25 dstore 10
27 return

3、窄化类型转换(Narrowing Numeric Conversion)

3.1、转换规则
3.2、精度损失问题
3.3、说明
3.4、实例
    /**
     * 窄化类型转换
     */
    public void downCast2() {
        int i = 10;
        byte b = (byte) i;
        short s = (short) i;
        char c = (char) i;

        long l = 10L;
        int i1 = (int) l;
        byte b1 = (byte) l;
    }
 0 bipush 10
 2 istore_1
 3 iload_1
 4 i2b
 5 istore_2
 6 iload_1
 7 i2s
 8 istore_3
 9 iload_1
10 i2c
11 istore 4
13 ldc2_w #2 <10>
16 lstore 5
18 lload 5
20 l2i
21 istore 7
23 lload 5
25 l2i
26 i2b
27 istore 8
29 return
3.5、精度问题
    public void downCast3() {
        double d = Double.NaN;
        int i   =   (int) d;
        System.out.println(d);
        System.out.println(i);
    }
NaN
0
3.5、无穷大窄化精度问题
    public void downCast4() {
        double d = Double.POSITIVE_INFINITY;
        long l = (long) d;
        int i = (int) d;
        System.out.println(d);
        System.out.println(Double.MAX_VALUE);

        System.out.println(l);
        System.out.println(Long.MAX_VALUE);

        System.out.println(i);
        System.out.println(Integer.MAX_VALUE);
    }
Infinity
1.7976931348623157E308
9223372036854775807
9223372036854775807
2147483647
2147483647

六、对象的创建与访问指令

Java是面向对象的程序设计语言,虚拟机平台从字节码层面就对面向对象做了深层次的支持。有一系列指令专门用于对象操作,可进一步细分为创建指令、字段访问指令、数组操作指令、类型检查指令

1、创建指令

虽然类实例和数组都是对象,但Java虚拟机对类实例和数组的创建与操作使用了不同的字节码指令

1.1、创建类实例的指令:new
1.2、创建数组的指令
1.3、实例
    public void newArray() {
        int[] intArray = new int[10];
        Object[] objectArray = new Object[10];
        int[][] intMintArray = new int[10][10];
        String[][] stringMintArray = new String[10][];
    }
 0 bipush 10
 2 newarray 10 (int)
 4 astore_1
 5 bipush 10
 7 anewarray #2 <java/lang/Object>
10 astore_2
11 bipush 10
13 bipush 10
15 multianewarray #3 <[[I> dim 2
19 astore_3
20 bipush 10
22 anewarray #4 <[Ljava/lang/String;>
25 astore 4
27 return
    public void newArray() {
        int[] intArray = new int[10];
        Object[] objectArray = new Object[10];
        int[][] intMintArray = new int[10][10];
        String[][] stringMintArray = new String[10][10];
    }
 0 bipush 10
 2 newarray 10 (int)
 4 astore_1
 5 bipush 10
 7 anewarray #2 <java/lang/Object>
10 astore_2
11 bipush 10
13 bipush 10
15 multianewarray #3 <[[I> dim 2
19 astore_3
20 bipush 10
22 bipush 10
24 multianewarray #4 <[[Ljava/lang/String;> dim 2
28 astore 4
30 return

2、字段访问指令

对象创建后,就可以通过对象访问指令获取对象实例或数组中的字段或者数组元素

实例
    public void setOrderId() {
        Order order = new Order();
        order.id = 1001;
        System.out.println(order.id);

        Order.name = "ORDER";
        System.out.println(Order.name);
    }
 0 new #8 <com/lkty/loadandstore/Order>
 3 dup
 4 invokespecial #9 <com/lkty/loadandstore/Order.<init>>
 7 astore_1
 8 aload_1
 9 sipush 1001
12 putfield #10 <com/lkty/loadandstore/Order.id>
15 getstatic #5 <java/lang/System.out>
18 aload_1
19 getfield #10 <com/lkty/loadandstore/Order.id>
22 invokevirtual #11 <java/io/PrintStream.println>
25 ldc #12 <ORDER>
27 putstatic #13 <com/lkty/loadandstore/Order.name>
30 getstatic #5 <java/lang/System.out>
33 getstatic #13 <com/lkty/loadandstore/Order.name>
36 invokevirtual #7 <java/io/PrintStream.println>
39 return

3、数组操作指令

3.1数组操作指令主要有:xastore和xaload指令
3.2、说明
实例
    public void arrayTest() {
        int[] intArray = new int[10];
        intArray[1] = 10;
        int indexValue = intArray[1];
    }
 0 bipush 10
 2 newarray 10 (int)
 4 astore_1
 5 aload_1
 6 iconst_1
 7 bipush 10
 9 iastore
10 aload_1
11 iconst_1
12 iaload
13 istore_2
14 return

4、类型检查指令

检查类实例或数组类型的指令:instanceof、checkcast

七、方法调用与返回指令

1、方法调用指令

1.1、invokevirtual指令
1.2、invokeInterface指令
1.3、invokespecial指令
1.4、invokestatic指令
1.5、invokedynamic指令

2、方法返回指令

方法调用结束前,需要进行返回。方法返回指令是“根据返回值的类型区分”的

八、操作数栈管理指令

如同操作一个普通数据结构中的堆栈那样,JVM提供的操作数栈管理指令,可以用于直接操作操作数栈的指令

1、操作数栈管理指令

2、说明

2.1、不带_x的指令是复制栈顶数据并压入栈顶。包括两个指令,dup和dup2,dup的系数代表要复制的Slot个数
2.2、带_x的指令是复制栈顶数据并插入栈顶一下的某个位置。共有4个指令,dup_x1,dup2_x1,dup_x2,dup2_x2。对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置。

九、控制转移指令

程序流程离不开条件控制,为了支持条件跳转,虚拟机提供了大量字节码指令,大体上可以分为:

1、比较指令

1.1、比较指令说明

2、条件跳转指令

3、比较条件跳转指令

4、多条件分支跳转指令

5、无条件跳转指令

十、异常处理指令

1、抛出异常指令

1.1、athrow指令

2、异常处理与异常表

2.1、处理异常
2.2、异常表

十一、同步控制指令

Java虚拟机支持两种同步结构:“方法级的同步”和“方法内部一段指令序列的同步”,这两种同步都是使用 monitor 来支持的

1、方法级的同步

2、方法内指定指令序列的同步

image.png
上一篇下一篇

猜你喜欢

热点阅读