JVM:字节码指令

前言

字节码指令是由一个字节长度的代表着某种特定操作含义的操作码以及跟随其后的零至多个代表此操作所需参数的操作数构成。

数据类型

大多数字节码指令都包含了其操作数据的类型信息,它们的操作码助记符中都有特殊的字符来表明专门为哪种数据类型服务,有一些字节码助记符中没有明确指出操作类型的特殊字符,也有一些字节码指令是与数据类型无关的。但是大多数字节码指令都不支持short、byte、char和boolean类型的操作,而是直接把这些类型转化为int类型。

特殊字符说明
iint
llong
sshort
bbyte
cchar
ffloat
ddouble
a引用类型

加载存储指令

加载存储指令的作用是将数据从栈帧的局部变量表和操作数栈之间来回传递。

局部变量入栈指令

[ilfda]load
[ilfda]load_<n> #n为0到3索引值

常量入栈指令

const系列

const系列用于对特定的常量入栈。

iconst_[n] #n从-1(m1)到5数据值
lconst_[n] #n从0到1数据值
fconst_[n] #n从0到2数据值
dconst_[n] #n从0到1数据值
aconst_null

push系列

bipush #接收8位整形做参数
sipush #接收16位整型做参数

ldc系列

ldc系列指令可以接收一个参数,该参数是指向常量池中具体值的索引。

ldc #8位索引
ldc_w #16位索引
ldc2_w #压入元素是long或double类型

出栈指令

出栈指令用于将操作数栈中栈顶元素弹出装入局部变量表的指定位置,用于给局部变量表赋值。

[ilfda]_store 
[ilfda]_store_n #n为0到3的索引

算术指令

算术指令用于将两个操作数栈上的值进行某种特定运算,并把结果压入操作数栈中。

运算时的溢出

数据运算可能会导致溢出,JVM没有明确规定整型数据溢出的具体结果,只规定了当除法指令和求余指令中出现除数为0时会导致虚拟机抛出ArithmeticException异常。

运算模式

  • 向最接近数舍入模式:在进行浮点运算时,非精确结果都必须舍入为可被表示的最近的精确值,如果有两种可表示的形式与该值一样接近,将优先选择最低有效位为零的。
  • 向零舍入模式:将浮点数转换为整数时采用该模式,该模式将在目标数值类型中选择一个最接近但是不大于原值的数字作为最精确的舍入结果。

NaN值使用

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

加法指令

[ilfd]add
iinc

减法指令

[ilfd]sub

乘法指令

[ilfd]mul

除法指令

[ilfd]div

求余指令

[ilfd]rem

取反指令

[ilfd]neg

位移指令

[il]shl
[il]shr
[il]ushr

逻辑指令

[il]or
[il]and
[il]xor

比较指令

比较栈顶两个操作数的大小,并将结果入栈。

dcmpg #遇见NaN值会压入1
dcmpl #遇见NaN值会压入-1
fcmpg
fcmpl
lcmp

类型转换指令

类型转换指令将两种不同的数值类型相互转换,一般用于实现用户代码中的显式类型转化操作或者用来处理字节码指令集中数据类型相关指令无法与数据类型一一对应的问题。

i2[bcs]
l2i
f2[il]
d2[ilf]

对象创建与访问指令

创建指令

对象创建

new 

数组创建

newarray #基本类型数组
anewarray #引用类型数组
multianewarray #多维数组

字段访问指令

类字段

getstatic #获取类字段
putstatic #存放类字段

实例字段

getfield  #获取实例字段
putfield #存放实例字段

数组操作指令

将数组元素加载到操作数栈

[bcsilfda]load

将操作数栈的值存放到数组

[bcsilfda]store

取数组长度

arraylength

类型检查指令

checkcast #用于检查强制类型转换是否可以进行,如果可以进行,那么该指令不会改变操作数栈,否则会抛出ClassCastException异常
instanceof #用于判断给定对象是否是某一个类的实例,它会将判断的结果压入操作数栈

操作数栈管理指令

操作数栈管理指令可以用于直接操作操作数栈。

pop,pop2 #弹出一个或两个元素
dup,dup2 #复制栈顶一个或两个元素并重新压入栈中
dup_x1,dup2_x1,dup_x2,dup2_x2 #复制栈顶一个或两个元素并将复制的元素插入栈中的某个位置,dup和x的系数相加即为要插入的位置
swap #将栈顶两个元素互换
nop #什么也不做

控制转移指令

条件跳转指令

ifeq #当栈顶int类型数值等于0时跳转
ifne #当栈顶int类型值不等于0时跳转
iflt #当栈顶int类型值小于0时跳转
ifle #当栈顶int类型值小于等于0时跳转
ifgt #当栈顶int类型数组大于0时跳转
ifge #当栈顶int类型数组大于等于0时跳转
ifnull #为null时跳转
ifnonnull #不为0时跳转

比较条件跳转指令

if_cmpeq #比较栈顶两int型数值大小,当前者等于后者时跳转
if_icmpne #比较栈顶两int型数值大小,当前者不等于后者时跳转
if_icmplt #比较栈顶两int型数值大小,当前者小于后者时跳转
if_icmple #比较栈顶两int型数值大小,当前者小于等于后者时跳转
if_icmpgt #比较栈顶两int型数值大小,当前者大于后者时跳转
if_icmpge #比较栈顶两int型数值大小,当前者大于等于后者时跳转
if_acmpeq #比较栈顶两引用类型数值,当结果相等时跳转
if_acmpne #比较栈顶两引用类型数值,当结果不相等时跳转

多条件分支跳转指令

tableswitch #用于switch条件跳转,case值连续
lookupswitch #用于switch条件跳转,case值不连续

无条件跳转指令

goto #无条件跳转
goto_w #无条件跳转(宽索引)
jsr #跳转至指定16位offset位置,并将jsr小一条指令地址压入栈顶
jsr_w #跳转至指定32位offset位置,并将jsr小一条指令地址压入栈顶
ret #返回至由指定的局部变量所给出的指令位置(一般与jsr,jsr_w联合使用)

方法调用和返回指令

方法调用指令

invokeinterface #|用于调用接口方法,它会在运行时搜索特定对象所实现的这个接口方法,并找出适合的方法进行调用
invokespecial #用于调用一些需要特殊处理的实例方法,包括`实例初始化方法,私有方法和类方法`,这些方法都是静态类型绑定的,不会在调用时进行动态派发
invokestatic #用于调用命名类中的static方法,这是静态绑定的
invokedynamic #该指令的分配逻辑是由用户所设定的引导方法决定的
invokevirtual #用于调用对象的实例方法,根据对象的实际类型进行分派,支持多态

方法返回指令

[ilfda]return 
return

异常处理指令

抛出异常指令

athrow #throw语句抛出的异常都是由该指令实现的

处理异常

处理异常并不是通过指令实现的,而是通过异常表实现的。当一个异常被抛出时,JVM会在当前的方法里寻找一个匹配的处理,如果没有找到这个方法会强制结束并弹出当前栈帧,并且异常会重新抛给上层调用的方法,如果在所有栈帧弹出前仍然没有找到合适的异常处理,这个线程将终止。如果这个异常在最后一个守护线程里抛出,将会导致JVM终止。不管什么时候抛出异常,如果异常处理匹配了所有异常类型,处理代码就会被执行。

同步控制指令

方法级的同步

方法级的同步是隐式的,即无需通过字节码指令来控制,它实现在方法调用和返回操作之前,虚拟机可以从方法常量池的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否声明为同步方法,如果设置了,执行线程将先持有同步锁,然后执行方法,最后在方法完成时释放同步锁,如果这个方法抛出了无法处理的异常,那么这个方法持有的锁将在抛出异常时自动释放。

代码块的同步

代码级的同步是显式的,JVM的指令集中有monitorentermonitorexit两条指令来支持synchronized关键字,当一个线程进入代码块时,它使用moniterenter指令请求进入,如果当前对象的监视计数器为0,则它会被准许进入,若为1则判断持有当前监视器的线程是否为自己,如果是则进入,如果不是则进行等待,直到对象的监视器计数器为0才会被允许进入同步块,当线程退出同步代码块时需要使用monitorexit声明退出,在JVM中,任何对象都有一个监视器与之相关联,用来判断对象是否被锁定,当监视器被持有后,对象处于被锁定状态,这两条指令执行时都需要在操作数栈压入对象之后。这两条指令的锁定和释放都是针对这个对象的监视器进行的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亻乍屯页女子白勺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值