您当前的位置:首页 > 计算机 > 编程开发 > Java

Java 字节码指令集

时间:12-14来源:作者:点击数:

Java 虚拟机的指令由一个字节长度的、代表着某种特定操作含义的操作码(Opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(Operands)所构成。

对于大部分为与数据类型相关的字节码指令,他们的操作码助记符中都有特殊的字符来表明专门为哪种数据类型服务:i 代表对 int 类型的数据操作,l 代表 long,s 代表 short,b 代表 byte,c 代表 char,f 代表 float,d 代表 double,a 代表 reference。

加载和存储指令

  • 将一个局部变量加载到操作栈的指令包括有:iloadiload_<n>lloadlload_<n>floadfload_<n>dloaddload_<n>aloadaload_<n>
  • 将一个数值从操作数栈存储到局部变量表的指令包括有:istoreistore_<n>lstorelstore_<n>fstorefstore_<n>dstoredstore_<n>astoreastore_<n>
  • 将一个常量加载到操作数栈的指令包括有:bipushsipushldcldc_wldc2_waconst_nulliconst_m1iconst_<i>lconst_<l>fconst_<f>dconst_<d>
  • 扩充局部变量表的访问索引的指令:wide

运算指令

运算之后的结果会自动入栈

  • 加法指令:iaddladdfadddadd
  • 减法指令:isublsubfsubdsub
  • 乘法指令:imullmulfmuldmul
  • 除法指令:idivldivfdivddiv
  • 求余指令:iremlremfremdrem
  • 取反指令:ineglnegfnegdneg
  • 位移指令:ishlishriushrlshllshrlushr
  • 按位或指令:iorlor
  • 按位与指令:iandland
  • 按位异或指令:ixorlxor
  • 局部变量自增指令:iinc
  • 比较指令:dcmpgdcmplfcmpgfcmpl、`lcmp

类型转换指令

Java 虚拟机对于宽化类型转换直接支持,并不需要指令执行,包括:

  • int 类型到 long、float 或者 double 类型
  • long 类型到 float、double 类型
  • float 类型到double类型
  • 窄化类型转换指令包括有:i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l 和 d2f。但是窄化类型转换很可能会造成精度丢失。

对象创建与操作指令

  • 创建类实例的指令:new
  • 创建数组的指令:newarray,anewarray,multianewarray
  • 访问类字段(static 字段,或者称为类变量)和实例字段(非static字段,或者成为实例变量)的指令:getfield、putfield、getstatic、putstatic
  • 把一个数组元素加载到操作数栈的指令:baload、caload、saload、iaload、laload、faload、daload、aaload
  • 将一个操作数栈的值储存到数组元素中的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore
  • 取数组长度的指令:arraylength
  • 检查类实例类型的指令:instanceof、checkcast

操作数栈管理指令

Java 虚拟机提供了一些用于直接操作操作数栈的指令,包括:pop、pop2、dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2和swap;

控制转移指令

  • 条件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt, if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne。
  • 复合条件分支:tableswitch、lookupswitch
  • 无条件分支:goto、goto_w、jsr、jsr_w、ret

方法调用和返回指令

  • invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是 Java 语言中最常见的方法分派方式。
  • invokeinterface 指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。
  • invokespecial 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法(§2.9)、私有方法和父类方法。
  • invokestatic指令用于调用类方法(static 方法)。

(上面4个invoke*调用之后的结果依旧是在操作栈的)

而方法返回指令则是根据返回值的类型区分的,包括有 ireturn(当返回值是boolean、byte、char、short 和 int 类型时使用)、lreturn、freturn、dreturn和areturn,另外还有一条 return 指令供声明为 void 的方法、实例初始化方法、类和接口的类初始化方法使用。

抛出异常指令

athrow

源代码经过编译器编译之后便会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是 JVM 的指令,而不像 C、C++ 经由编译器直接生成机器码。我们不用担心生成的字节码文件的兼容性,因为所有的 JVM 全部遵守 Java 虚拟机规范,也就是说所有的JVM环境都是一样的,这样一来字节码文件可以在各种JVM上运行。 当然也包括KVM。

每一个线程都有一个保存帧的栈。在每一个方法调用的时候创建一个帧。一个帧包括了三个部分:操作栈,局部变量数组,和一个对当前方法所属类的常量池的引用。

局部变量数组也被称之为局部变量表,它包含了方法的参数,也用于保存一些局部变量的值。参数值得存放总是在局部变量数组的 index0 开始的。如果当前帧是由构造函数或者实例方法创建的,那么该对象引用将会存放在 location0 处,然后才开始存放其余的参数。

局部变量表的大小由编译时决定,同时也依赖于局部变量的数量和一些方法的大小。操作栈是一个(LIFO)栈,用于压入和取出值,其大小也在编译时决定。某些opcode指令将值压入操作栈,其余的 opcode 指令将操作数取出栈。使用它们后再把结果压入栈。操作栈也用于接收从方法中返回的值。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐