bilibili-JVM学习笔记16
The Java Virtual Machine Specification - Java SE 8 Edition

JVM学习笔记11 - Java字节码初识JVM学习笔记12 - 解读笔记11中的attributesJVM学习笔记13JVM学习笔记14 异常JVM学习笔记15 方法执行

编译执行 解释执行

  • 现代 JVM 在执行 Java 代码的时候,通常都会将解释执行与编译执行二者结合起来进行;
  • 解释执行
  • 通过 jvm 解释器来读取字节码,遇到相应的字节码指令就去执行该指令;
  • 编译执行
  • 通过即时编译器(Just In Time, JIT)将字节码指令转换为本地机器码来执行;现代 JVM 会根据代码热点来生成相应的本地机器码;
  • 失去了可移植性的优点
  • 提高了执行效率
  • 基于栈的指令集与基于寄存器的指令集之间的关系
  • JVM 执行字节码指令所采取的方式是基于栈的指令集
  • 主要操作有入栈和出栈;
  • 优点:可以在不同平台之间移植
  • 缺点:完成相同的操作,指令数量通常比基于寄存器的指令集数量要多;在内存中完成执行指令的操作,速度慢很多;
  • 虽然虚拟机可以采用一些优化手段,但总体来说,基于栈的指令集的执行还是要慢一些的;
  • 基于寄存器的指令集
  • 与硬件架构紧密关联,无法做到可移植;
  • 基于寄存器的指令集是直接由 CPU 来执行的,它是在高速缓冲区中进行执行的,速度比在内存中执行高出一个数量级;

JVM执行栈指令集实例剖析

java 源码

package new_package.jvm.p55;

public class MyTest {

    public int cal() {
        int a = 6;
        int b = 1;
        int c = 5;
        int d = 3;
        int result = (a + b - c) * d;
        return result;
    }

    public static void main(String[] args) {
        System.out.println(new MyTest().cal());
    }
}

javap -v new_package.jvm.p55.MyTest

cal 方法相关的字节码

public int cal();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=6, args_size=1
         0: bipush        6
         2: istore_1
         3: iconst_1
         4: istore_2
         5: iconst_5
         6: istore_3
         7: iconst_3
         8: istore        4
        10: iload_1
        11: iload_2
        12: iadd
        13: iload_3
        14: isub
        15: iload         4
        17: imul
        18: istore        5
        20: iload         5
        22: ireturn
      LineNumberTable:
        line 6: 0
        line 7: 3
        line 8: 5
        line 9: 7
        line 10: 10
        line 11: 20
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      23     0  this   Lnew_package/jvm/p55/MyTest;
            3      20     1     a   I
            5      18     2     b   I
            7      16     3     c   I
           10      13     4     d   I
           20       3     5 result   I

解析:

  1. stack=2 表示当前方法的操作数栈最大深度为 2
  2. locals=6 表示当前方法局部变量表长度为 6
  3. args_size=1 表示当前方法入参数量为 1 ;(实例方法的第一个隐形入参 this ,指向当前实例)

接下来对字节码指令逐个解释:

  1. bipush 6

bipush :将一个 int 类型的数值压入操作数栈

此处是将数值 6 压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_JVM


2. istore_1

istore_1 : istore 1 的简写;
将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 1 的位置处

jvm能够直接运行java字节码 jvm如何执行字节码指令_指令集_02

  1. iconst_1

iconst_1 : bipush 1 的简写

此处是将数值 1 压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_指令集_03

  1. istore_2

将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 2 的位置处

jvm能够直接运行java字节码 jvm如何执行字节码指令_JVM_04

  1. iconst_5

此处是将数值 5 压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_jvm能够直接运行java字节码_05

  1. istore_3

将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 3 的位置处

jvm能够直接运行java字节码 jvm如何执行字节码指令_jvm能够直接运行java字节码_06

  1. iconst_3

此处是将数值 3 压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_操作数_07

  1. istore 4

将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 4 的位置处

jvm能够直接运行java字节码 jvm如何执行字节码指令_指令集_08

  1. iload_1

iload_1 : iload 1 的简写

iload 从局部变量中加载一个 int 类型的值

iload_1 即从索引 1 的位置加载一个值,压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_jvm能够直接运行java字节码_09

  1. iload_2

iload_2 即从索引 2 的位置加载一个值,压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_操作数_10

  1. iadd

从操作数栈中弹出两个值(int 类型的值),然后相加,将结果压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_JVM_11

  1. iload_3

从索引 3 的位置加载一个值,压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_JVM_12

  1. isub

从操作数栈中弹出两个数(int 类型),第一个为减数,第二个为被减数,结果为被减数 - 减数,将结果重新压入操作数栈;

jvm能够直接运行java字节码 jvm如何执行字节码指令_操作数_13

  1. iload 4

从索引 4 的位置加载一个值,压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_操作数_14

  1. imul

两个 int 类型的值相乘,并将结果重新压入操作数栈;

jvm能够直接运行java字节码 jvm如何执行字节码指令_JVM_15

  1. istore 5

将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 5 的位置处

jvm能够直接运行java字节码 jvm如何执行字节码指令_指令集_16

  1. iload 5

从索引 5 的位置加载一个值,压入操作数栈

jvm能够直接运行java字节码 jvm如何执行字节码指令_操作数_17

  1. ireturn

从当前帧的操作数栈中弹出值,并将其压入调用者的帧操作数栈