【深入理解Java虚拟机】字节码指令

一:字节码指令简介

Java虚拟机的指令由一个字节长度的,代表着某种特定操作含义的数字(简称操作码,Opcode)以及跟随其后的零个至多个代表此操作所需参数(操作数Operands)构成。由于Java虚拟机采用面向操作数栈而不是寄存器的架构,所以大多数指令都不包含操作数,只有一个操作码。

特点:操作码长度为一个字节(即0到255)

如果将16位长度的无符号整数使用两个无符号字节存储,将它们命名为byte1和byte2,那么它们的值应该是这样的:
(byte1 << 8)| byte2
其特点 损失一些性能,放弃长度对齐,可以省略很多填充和间隔符号

如果不考虑异常处理的话,java虚拟机的解释器可以使用以下伪代码当做最基本的执行模型来理解:
do{
	自动计算pc寄存器的值加1;
	根据pc寄存器的指示位置,从字节码流中取出操作码;
	if(字节码存在操作数)  从字节码流中取出操作数;
	执行操作码所定义的操作;
} while( 字节码流长度 > 0 );

二:字节码与数据类型

由于java中的操作码长度只有个字节,所以必然,并不会所有的类型都有对应的操作。
在这里插入图片描述

1.加载和存储指令

用于将数据从栈帧中的局部变量表和操作数栈之间的来回传输。
a). 将一个局部变量加载到操作栈:iload、iload_、lload、lload_、fload、fload_、dload、dload_、aload、aload_。
b).将一个数值从操作数栈存储到局部变量表:istore、istore_、lstore、lstore_、fstore、fstore_、dstore、dstore_、astore、astore_。
c).将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_、lconst_、fconst_、dconst_。
d).扩充局部变量的访问索引的指令:wide。

2.运算指令

用于对两个操作数栈上的值进行某种特定的运算,并把结果重新存入到操作数栈顶。
加法指令:iadd、ladd、fadd、dadd。
减法指令:isub、lsub、fsub、dsub。
乘法指令:imul、lmul、fmul、dmul。
除法指令:idiv、ldiv、fdiv、ddiv。
求余指令:irem、lrem、frem、drem。
取反指令:ineg、lneg、fneg、dneg。
位移指令:ishl、ishr、iushr、lshl、lshr、lushr。
按位或指令:ior、lor。
按位与指令:iand、land。
按位异或指令:ixor、lxor。
局部变量自增指令:iinc。
比较指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp。

3.类型转换指令

用于将两种不同的数值类型进行相互转换,主要包括显示类型转换的操作,或者处理字节码指令集数据类型相关指令无法与数据类型一一对应的问题。
Java 虚拟机直接支持(即转换时无需显示的转换指令)以下数值类型的宽化类型转换(Widening Numeric Conversions,即小范围类型向大范围类型的安全转换):
a.int 类型到 long、float 或者 double 类型。
b.long 类型到 float、double 类型。
c.float 类型到 double 类型。
窄化类型转换,会损失精度,但永远不会抛出异常。

4.对象的创建和访问指令

创建类实例的指令: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。

6.操作数栈管理指令

直接操作操作数栈:
a.将操作数栈的栈顶一个或两个元素出栈:pop、pop2。
b.复制栈顶一个或两个数值并将复制值或双份的复制重新压入栈顶:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2。
c.将栈最顶端的两个数值互换:swap。

7.控制转移指令

让java虚拟机有条件或者无条件从指定位置指令而不是控制转移指令的下一条指令继续执行的程序。从概念模型上理解,可以认为控制转移指令就是在有条件或无条件地修改 PC 寄存器的值。控制转移指令如下。
a.条件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt、if_icmpgt、if_icmple、if_icmpge、if_acmpeq 和 if_acmpne。
b.复合条件分支:tableswitch、lookupswitch。
c.无条件分支:goto、goto_w、jsr、jsr_w、ret。

8.方法调用和返回指令

方法调用(分派、执行过程),先列举以下 5 条用于方法调用的指令:
a.invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是 Java 语言中最常见的方法分派方式。
b.invokeinterface 指令用于调用接口方法,它会在运行时搜索一下实现了这个接口方法的对象,找出适合的方法进行调用。
c,invokespecial 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。
d.invokestatic 指令用于调用类方法(static 方法)。
e.invokedynamic 指令用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法,前面 4 条调用指令的分派逻辑都固化在 Java 虚拟机内部,而 invokedynamic 指令的分派逻辑是由用户所设定的引导方法决定的。
方法调用指令与数据类型无关,而方法返回指令是根据返回值的类型区分的,包括 ireturn(当返回值是 boolean、byte、char、short 和 int 类型时使用)、lreturn、freturn、dreturn 和 areturn,另外还有一条return 指令共声明为 void 的方法、实例初始化方法以及类和接口的类初始化方法使用。

9.异常处理指令

显示抛出异常的操作(throw语句)都由athrow指令来实现。 以及内部定义运行时异常的自动抛出。

10.同步指令

Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor)来支持的。
1)方法级同步:隐式的,无需通过字节码指令控制,它实现在方法调用和返回操作之中。虚拟机可以从方法常量池的方法表结构中的ACC_SYNCHRONIZED访问标志得知该方法是否被同步。
2)指定序列同步:通过synchronized语句块来表示,java虚拟机指令集中有monitorenter和monitorexit两条指令来支持synchronized关键字的语义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值