[三] java虚拟机 JVM字节码 指令集 bytecode 操作码 指令分类用法 助记符
指令简介
do{ 自动计算pc寄存器以及从pc寄存器的位置取出操作码; if(存在操作数){ 取出操作数; } 执行操作码所定义的操作; }while(处理下一次循环);
指令详解
数据类型
byte | short | int | long | float | double | char | reference |
boolean |
b |
s |
i |
l |
f |
d |
c |
a |
无 |
逻辑功能
加载存储指令 |
算数指令 |
类型转换指令 |
对象的创建于操作 |
操作数栈管理指令 |
控制转移指令 |
方法调用和返回指令 |
抛出异常 |
同步 |
指令-相关计算机英语词汇含义
push |
push |
按 推动 压入 |
load |
load |
加载 装载 |
const |
const |
常数,不变的 |
store |
store |
存储 保存到 |
add |
add |
加法 |
sub |
subduction |
减法 |
mul |
multiplication |
乘法 |
div |
division |
除法 |
inc |
increase |
增加 |
rem |
remainder |
取余 剩下的留下的 |
neg |
negate |
取反 否定 |
sh |
shift |
移位 移动变换 |
and |
and |
与 |
or |
or |
或 |
xor |
exclusive OR |
异或 |
2 |
to |
转换 转变 变成 |
cmp |
compare |
比较 |
return |
return | 返回 |
eq |
equal |
相等 |
ne |
not equal |
不相等 |
lt |
less than |
小于 |
le |
less than or equal |
小于等于 |
gt |
greater than |
大于 |
ge |
greater than or equal |
大于等于 |
if |
if |
条件判断 如果 |
goto |
goto |
跳转 |
invoke |
invoke |
调用 |
dup |
dump |
复制 拷贝 卸下 丢下 |
指令-数据类型相关的指令
java中的操作码长度只有个字节,所以必然,并不会所有的类型都有对应的操作
Java虚拟机指令集对于特定的操作只提供了有限的类型相关指令
有一些单独的指令可以再必要的时候用来将一些不支持的类型转换为可支持的类型
|
操作码/类型 |
byte |
short |
int |
long |
float |
double |
char |
reference |
Tipush |
bipush |
sipush |
||||||
Tconst |
iconst |
lconst |
fconst |
dconst |
aconst |
|||
Tload |
iload |
lload |
fload |
dload |
aload |
|||
Tstore |
istore |
lstore |
fstore |
dstore |
astore |
|||
Tinc |
iinc |
|||||||
Taload | baload | saload | iaload | laload | faload | daload | caload | aaload |
Tastore | bastore | sastore | iastore | lastore | fastore | dastore | castore | aastore |
Tadd | iadd | ladd | fadd | dadd |
||||
Tsub | isub | lsub | fsub | dsub |
||||
Tmul | imul |
lmul | fmul | dmul |
||||
Tdiv | idiv | ldiv | fdiv | ddiv |
||||
Trem | irem | lrem | frem | drem |
||||
Tneg | ineg | lneg | fneg | dneg |
||||
Tshl |
ishl |
lshl |
||||||
Tshr |
ishr |
lshr |
||||||
Tushr | iushr | lushr |
||||||
Tand | iand | land |
||||||
Tor |
ior | lor |
||||||
Txor | ixor | lxor |
||||||
i2T | i2b | i2s | i2l | i2f | i2d |
|||
l2T | l2i | l2f | l2d |
|||||
f2T | f2i | f2l | f2d |
|||||
d2T |
d2i | d2l | d2f |
|||||
Tcmp |
lcmp |
|||||||
Tcmpl |
fcmpl | dcmpl |
||||||
Tcmpg | fcmpg | dcmpg |
||||||
if_TcmpOP |
if_icmpOP | if_acmpOP |
||||||
Treturn |
ireturn | lreturn | freturn | dreturn | areturn |
另外需要格外注意的是,上表是为了呈现部分与数据类型相关联的操作码
并不是说所有的操作码都在上表中,仅仅是和数据类型相关联的才出现在了上表中
|
实际类型 |
运算类型 |
分类 |
boolean |
int |
1 |
int |
int |
1 |
byte |
int |
1 |
short |
int |
1 |
int |
int |
1 |
float |
float |
1 |
reference |
reference |
1 |
returnAddress |
returnAddress |
1 |
long | long | 2 |
double |
double |
2 |
按照逻辑功能进行划分
加载存储指令
算数指令
运算后的结果自动入栈
运算或算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶.
算术指令分为两种:整型运算的指令和浮点型运算的指令.
无论是哪种算术指令,都使用Java虚拟机的数据类型
由于没有直接支持byte、short、char和boolean类型的算术指令,使用操作int类型的指令代替.
|
加法指令: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
|
再次强调 加add 减sub 乘mul 除div 求余rem 取反neg 移位sh l r表示左右 与and 或or 异或xor 自增inc cmp比较 |
加 减 乘 除 求余 取反 支持 <int i long l float f double d> 四种类型 理解点:常用操作支持四种常用类型 byte short char boolean使用int 移位运算与按位与或异或运算 支持< int i long l > 自增支持< int i > |
补充说明:
关于移位运算,
左移只有一种:
规则:丢弃最高位,往左移位,右边空出来的位置补0
右移有两种:
1. 逻辑右移:丢弃最低位,向右移位,左边空出来的位置补0
2. 算术右移:丢弃最低位,向右移位,左边空出来的位置补原来的符号位(即补最高位)
移位运算的u表示的正是逻辑移位 d 和f开头 分别代表double 和float的比较 |
类型转换指令
类型转换指令可以将两种不同的数值类型进行相互转换。
这些转换操作一般用于实现用户代码中的显式类型转换操作
或者用来解决字节码指令集不完备的问题
因为数据类型相关指令无法与数据类型一一对应的问题,比如byte short char boolean使用int, 所以必须要转换
|
分为宽化 和 窄化 含义如字面含义,存储长度的变宽或者变窄 宽化也就是常说的安全转换,不会因为超过目标类型最大值丢失信息 窄化则意味着很可能会丢失信息 宽化指令和窄化指令的形式为 操作类型 2 (to) 目标类型 比如 i2l int 转换为long |
宽化指令
int类型到long、float或者double类型
long类型到float、double类型
float类型到double类型
i2l、i2f、i2d
l2f 、l2d
f2d
|
窄化指令 int类型到byte short char类型 long类型到int类型 float类型到int或者long类型 从double类型到int long 或者float类型 i2b 、i2s 、i2c l2i f2i 、f2l d2i 、d2l 、d2f |
对象的创建与访问
实例和数组都是对象 但是Java虚拟机对类实例和数组的创建使用了不同的字节码指令 |
涉及到对象的创建与访问的相关操作有: 1.创建实例对象/数组 2.访问实例变量和类变量 3.加载与存储,对于类实例属于引用类型存取使用加载存储指令,所以此处只有数组有相关操作了 4.还有一些附属信息 数组长度以及检查类实例或者数组类型 |
创建类实例 : new 创建数组的指令 : newarray 分配数据成员类型为基本数据类型的新数组 anewarray 分配数据成员类型为引用类型的新数组 multianewarray 分配新的多维数组 |
类变量声明的时候使用static关键字 访问与存储类中的静态字段也是使用static关键字 getstatic 从类中获取静态字段 putstatic 设置类中静态字段的值 普通的成员实例变量使用field指代 getfield 从对象中获取字段值 putfield 设置对象中的字段的值 |
访问与存储之前介绍过 使用的load 和store 数组也是对象 引用使用a来表示 所以对于数组的存取和访问指令 使用 类型+a+load 或者store 的形式 把一个数组元素加载到操作数栈的指令: byte char short int long float double reference 对应的指令分别是 baload caload saload iaload laload faload daload aaload 把一个操作数栈的值存储到数组元素中的指令: byte char short int long float double reference
对应的指令分别是:
bastore castore sastore iastore lastore fastore dastore aastore
|
获取数组长度的指令 arraylength 检查类实例或者数组类型的指令 instanceof checkcast |
操作数栈管理指令
控制转移指令
控制转移指令可以让Java虚拟机有条件或者无条件的从指定的位置指令继续执行程序 而不是当前控制转移指令的下一条 |
控制转移指令包括
条件转移 复合条件转移以及无条件转移
boolean byte short char都是使用int类型的比较指令
long float double 类型的条件分支比较,会先执行相应的比较运算指令,运算指令会返回一个整型数值到操作数栈中
随后在执行int类型的条件分支比较操作来完成整个分支跳转
显然,虚拟机会对int类型的支持最为丰富
所有的int类型的条件分支指令进行的都是有符号的比较
|
long float double 类型的比较指令 lcmp fcmpl fcmpg dcmpl dcmpg 这五个都比较栈顶上面两个 指定类型的元素,然后将结果 [-1 0 1] 压入栈顶 cmpl与cmpg区别在于对NaN的处理,有兴趣的可以查看Java虚拟机规范 |
条件跳转指令
接下来这六个也就是上面说的配合long float 和double类型条件分支的比较
他们会对当前栈顶元素进行操作判断,只有栈顶的一个元素作为操作数
ifeq 当栈顶int类型元素 等于0时 ,跳转
ifne 当栈顶int类型元素 不等于0 时,跳转
iflt 当栈顶int类型元素 小于0 时,跳转
ifle 当栈顶int类型元素 小于等于0 时,跳转
ifgt 当栈顶int类型元素 大于0 时,跳转
ifge 当栈顶int类型元素 大于等于0 时,跳转
|
类似上面的long float double
int类型 和 reference 当然也有对两个操作数的比较指令,而且还一步到位了
if_icmpeq 比较栈顶两个int类型数值的大小 ,当前者 等于 后者时,跳转
if_icmpne 比较栈顶两个int类型数值的大小 ,当前者 不等于 后者时,跳转
if_icmplt 比较栈顶两个int类型数值的大小 ,当前者 小于 后者时,跳转
if_icmple 比较栈顶两个int类型数值的大小 ,当前者 小于等于 后者时,跳转
if_icmpge 比较栈顶两个int类型数值的大小 ,当前者 大于等于 后者时,跳转
if_icmpgt 比较栈顶两个int类型数值的大小 ,当前者 大于 后者时,跳转
if_acmpeq 比较栈顶两个引用类型数值的大小 ,当前者 等于 后者时,跳转
if_acmpne 比较栈顶两个引用类型数值的大小 ,当前者 不等于 后者时,跳转
|
复合条件跳转指令 tableswitch switch 条件跳转 case值连续 lookupswitch switch 条件跳转 case值不连续 |
无条件转移指令 goto 无条件跳转 goto_w 无条件跳转 宽索引 jsr SE6之前 finally字句使用 跳转到指定16位的offset,并将jsr下一条指令地址压入栈顶 jsr_w SE6之前 同上 宽索引 ret SE6之前返回由指定的局部变量所给出的指令地址(一般配合jsr jsr_w使用) w同局部变量的宽索引含义 |
方法调用和方法返回指令
方法调用和方法返回指令 |
方法调用分为
实例方法接口方法 调用父类私有实力初始化等特殊方法,类静态方法等
以下5条指令用于方法调用:
invokevirtual指令用于调用对象的实例方法
invokeinterface指令用于调用接口方法,它会在运行时搜索由特定对象所实现的这个接口方法,并找出适合的方法进行调用。
invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。
invokestatic指令用于调用类方法(static方法)
invokedynamic 调用动态链接方法 比较复杂,稍后有时间会专门讲解
|
方法的调用与数据类型无关 但是方法的返回指令根据返回值类型进行区分 ireturn boolean byte char short int类型使用 lreturn long freturn float dreturn double areturn reference return void方法 实例初始化方法(构造方法) 类和接口的类初始化方法 |
异常指令
异常处理指令 |
Java程序中显式抛出异常的操作 throw语句,都是由athrow 指令来实现的 除了throw语句显式的抛出异常情况之外,Java虚拟机规范还规定了许多运行时异常 会在其他Java虚拟机指令检测到异常情况时,自动抛出 |
同步指令
同步指令 |
同步一段指令集序列通常是由Java语言中的synchronized 语句块来表示的, Java虚拟机的指令集中有monitorenter monitorexit (monitor +enter/exit) |