[三] java虚拟机 JVM字节码 指令集 bytecode 操…
2018-09-01 05:40:41来源:博客园 阅读 ()
指令简介
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 |
按照逻辑功能进行划分
加载存储指令
加载存储指令用于局部变量与操作数栈交换数据 以及常量装载到操作数栈 |
1、将一个局部变量加载到操作栈:
iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>
操作数为局部变量的位置序号 序号从0开始 , 局部变量以slot为单位分配的
将序号为操作数的局部变量slot 的值 加载到操作数栈
指令可以读作:将第(操作数+1)个 X(i l f d a)类型局部变量,推送至栈顶
ps: 操作数+1 是因为序号是从0开始的
2、将一个数值从操作数栈存储到局部变量表:
istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>
操作数为局部变量的位置序号 序号从0开始 , 局部变量以slot为单位分配的
将操作数栈的值保存到序号为操作数的局部变量slot中
指令可以读作:将栈顶 X(i l f d a)类型的数值 保存到 第(操作数+1)个 局部变量中
3、将一个常量加载到操作数栈:
bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>
操作数为将要操作的数值 或者常量池行号
指令可以读作:将类型X的值xxx 推送至栈顶 或者是 将 行号为xxx的常量推送至栈顶
4、扩充局部变量表的访问索引的指令:wide
|
形如 xxx_<n>以尖括号结尾的代表了一组指令 (例如 iload_<n> 代表了iload_0 iload_1 iload_2 iload_3)
这一组指令都是某个带有一个操作数的通用指令(例如 iload)的特殊形式
对于这些特殊形式来说,他们表面上没有操作数,但是操作数隐含在指令里面了,除此之外,语义与原指令并没有任何的不同
(例如 iload_0 的语义与操作数为0时的iload 语义完全相同)
<>尖括号中的字母表示了指令隐含操作数的数据类型
<n>表示非负整数 <i>表示int <l> 表示long <f> float <d> double 而byte char short类型数据经常使用int来表示
下划线 _ 的后面紧跟着的值就是操作数
|
需要注意的是 _<n> 的形式不是无限的,对于load 和 store系列指令
对于超过4个,也就是第5个,也就是下标是4 往后
都是直接只用原始形式 iload 4 不再使用_<n>的形式 所以你不会看到 load_4 load_5....或者store_4 store_5...
|
对于虚拟机执行方法来说,操作数栈是工作区, 所以数据的流向是对于他 操作数栈 来说的
load就是局部变量数据加载到操作数栈
store就是从操作数栈存储到局部变量表
对于常量只有加载到操作数栈进行使用,没有存储的说法,他也比较特殊
对于上图中的数据交换模型中,操作数栈是可以确定的也是唯一的,栈就在那里,不管你见或不见
对于操作数栈与局部变量交换数据时,需要确定的是 从 哪个局部变量取数据 或者保存到哪个局部变量中
所以load 和 store的操作数都是局部变量的位置
对于操作数栈与常量交换数据,需要确定的是到底加载哪个值到操作数栈或者是从常量池哪行加载
所以加载常量到操作数栈的操作数 是 具体的数值 或者常量池行号
|
常量加载到操作数栈比较特殊单独说明 他根据<数据类型>以及<数据的取值范围>使用了不同的方式 const指令 该系列命令主要负责把简单的数值类型送到栈顶。
该系列命令不带参数。只把简单的数值类型送到栈顶时,才使用如下的命令。
比如对应int型该方式只能把-1,0,1,2,3,4,5(分别采用iconst_m1,iconst_0, iconst_1, iconst_2, iconst_3, iconst_4, iconst_5)
送到栈顶。对于int型,其他的数值请使用push系列命令(比如bipush)
指令码 助记符 说明
0x01 aconst_null 将null推送至栈顶
0x02 iconst_m1 将int型(-1)推送至栈顶
0x03 iconst_0 将int型(0)推送至栈顶
0x04 iconst_1 将int型(1)推送至栈顶
0x05 iconst_2 将int型(2)推送至栈顶
0x06 iconst_3 将int型(3)推送至栈顶
0x07 iconst_4 将int型(4)推送至栈顶
0x08 iconst_5 将int型(5)推送至栈顶
0x09 lconst_0 将long型(0)推送至栈顶
0x0a lconst_1 将long型(1)推送至栈顶
0x0b fconst_0 将float型(0)推送至栈顶
0x0c fconst_1 将float型(1)推送至栈顶
0x0d fconst_2 将float型(2)推送至栈顶
0x0e dconst_0 将double型(0)推送至栈顶
0x0f dconst_1 将double型(1)推送至栈顶
简言之 取值 -1~5 时,JVM采用const指令将常量压入栈中 push指令
该系列命令负责把一个整型数字(长度比较小)送到到栈顶。
该系列命令有一个参数,用于指定要送到栈顶的数字。
注意该系列命令只能操作一定范围内的整形数值,超出该范围的使用将使用ldc命令系列。
指令码 助记符 说明
0x10 bipush 将单字节的常量值(-128~127)推送至栈顶
0x11 sipush 将一个短整型常量值(-32768~32767)推送至栈顶
ldc系列
该系列命令负责把数值常量或String常量值从常量池中推送至栈顶。
该命令后面需要给一个表示常量在常量池中位置(编号)的参数 也就是行号,
哪些常量是放在常量池呢?
比如:
final static int id=32768; //32767+1 就不在sipush范围内了
final static float double=8.8
对于const系列命令和push系列命令操作范围之外的数值类型常量,都放在常量池中.
另外,所有不是通过new创建的String都是放在常量池中的
指令码 助记符 说明
0x12 ldc 将int, float或String型常量值从常量池中推送至栈顶
0x13 ldc_w 将int, float或String型常量值从常量池中推送至栈顶(宽索引)
0x14 ldc2_w 将long或double型常量值从常量池中推送至栈顶(宽索引)
ps:所谓宽索引是指常量池行号 索引的字段长度, ldc 的索引只有8位 ldc_w的索引则有16位
对于宽索引,指令格式为 ldc_w ,indexbyte1,indexbyte2 会计算 (indexbyte1<<8) | indexbyte2 来生成一个指向当前常量池的无符号16位索引
说白了就是寻址长度
简言之就是对于绝大多数的数值,都是存放在常量池中的 将需要使用ldc
对于一小部分可能比较常用的数值,则是可以直接把值当做操作数的 使用const 或者push
|
wide的含义 宽索引 字节码的指令是单字节的,对于局部变量来说,最多容纳256个局部变量 wide指令就是用于扩展局部变量数的 ,将8位的索引在扩展8位 也就是16位 最多65536 形式为 wide 要被扩展的操作码比如iload 操作数 (wide iload 257 也就是 wide iload byte1 byte2) iload操作码是作为wide 操作码的一个操作数来执行的 wide可以修饰 load store ret 如果wide修饰的是iinc 格式有些变化 wide iinc byte1 byte2 constbyte1 constbyte2 本身 iinc为 iinc byte constbyte 扩展后的前两个字节16位为局部变量索引 后两个字节16位计算为 16位带符号的增量 计算的形式依旧是 (constbyte1 << 8) | constbyte2 |
算数指令
运算后的结果自动入栈 运算或算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶.
算术指令分为两种:整型运算的指令和浮点型运算的指令.
无论是哪种算术指令,都使用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 > 理解点: 移位与位运算支持整型,byte short char boolean使用int 另外还有long 自增支持< int i > |
补充说明: 关于移位运算,
左移只有一种:
规则:丢弃最高位,往左移位,右边空出来的位置补0
右移有两种:
1. 逻辑右移:丢弃最低位,向右移位,左边空出来的位置补0
2. 算术右移:丢弃最低位,向右移位,左边空出来的位置补原来的符号位(即补最高位)
移位运算的u表示的正是逻辑移位d 和f开头 分别代表double 和float的比较 cmpg 与cmpl 的唯一区别在于对NaN的处理,更多详细内容可以查看虚拟机规范的相关指令 lcmp 比较long类型的值 |
类型转换指令
类型转换指令可以将两种不同的数值类型进行相互转换。
这些转换操作一般用于实现用户代码中的显式类型转换操作
或者用来解决字节码指令集不完备的问题
因为数据类型相关指令无法与数据类型一一对应的问题,比如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 |
操作数栈管理指令
操作数栈管理指令,顾名思义就是直接用于管理操作栈的 |
出栈, 分为将操作数栈栈顶的几个元素出栈,一个元素或者两个元素 pop表示出栈, 数值代表个数 pop pop2 交换 将栈顶端的两个数值进行交换
swap
|
dup比较复杂一点
根本含义为复制栈顶的元素然后压入栈
不过涉及到复制几个元素,以及操作数栈的数据类型,所以比较复杂
上面提到过虚拟机处理的数据类型,有分类,分为1 和2两种类型
虚拟机能处理的类型long和double为类型2 其余为类型1 也就是int returnAddress reference等
dup 复制操作数栈栈顶一个元素 并且将这个值压入到栈顶 value必须分类1
形式如下,右侧为栈顶
... , value
... , value , value
dup_x1 复制操作数栈栈顶的一个元素.并插入到栈顶以下 两个值之后
形式如下,右侧为栈顶,value1 插入到了第二个元素value2 下面 value1 和value2 必须分类1
... , value2, value1
... , value1, value2, value1
dup_x2 复制操作数栈栈顶的一个元素. 并插入栈顶以下 2 个 或 3个值之后
形式一 如果 value3, value2, value1 全都是分类1 使用此形式 插入栈顶三个值 以下 也就是value3之下
..., value3, value2, value1 →
..., value1, value3, value2, value1
形式二如果value1 是分类1 value2 是分类2 那么使用此形式 插入栈顶两个值 以下,也就是value2 之下
..., value2, value1 →
..., value1, value2, value1
dup2 复制操作数栈栈顶一个或者两个元素,并且按照原有顺序,入栈到操作数栈
形式一 如果 value2, value1 全都是分类1 使用此形式 复制栈顶两个元素,按照原顺序,插入到栈顶
..., value2, value1 →
..., value2, value1, value2, value1
形式二 如果value 属于分类2 使用此形式 复制栈顶一个元素,插入到栈顶
..., value →
..., value, value
dup2_x1复制操作数栈栈顶一个或者两个元素,并且按照原有顺序 插入栈顶以下 两个或者三个 值 之后
形式一 如果 value3, value2, value1 都是分类1 使用此形式 复制两个元素,插入栈顶下 三个值之后,也就是value3 之后
..., value3, value2, value1 →
..., value2, value1, value3, value2, value1
形式二 如果value1 是分类2 value2 是分类1 使用此形式 复制一个元素,插入到栈顶以下 两个元素之后
..., value2, value1 →
..., value1, value2, value1
dup_x2 复制操作数栈栈顶一个或者两个元素,并且按照原有顺序 插入栈顶以下 两个或者三个 或者四个 值 之后
形式一 全都是分类1 使用此形式 复制两个元素,插入到栈顶 第四个值后面
..., value4, value3, value2, value1 →
..., value2, value1, value4, value3, value2, value1
形式二 如果 value1 是分类2 value2 和 value3 是分类1 中的数据类型 使用此形式 复制一个元素 插入到栈顶 第三个值后面
..., value3, value2, value1 →
..., value1, value3, value2, value1
形式三 如果value 1 value2 是分类1 value3 是分类2 使用此形式 复制两个元素 插入到栈顶 第三个值后面
..., value3, value2, value1 →
..., value2, value1, value3, value2, value1
形式四 当value1 和value2 都是分类2 使用此形式 复制一个元素 插入到栈顶 第二个值后面
..., value2, value1 →
..., value1, value2, value1
|
上面关于dup的描述摘自 虚拟机规范,很难理解 看起来是非常难以理解的,不妨换一个角度 我们知道局部变量的空间分配分为两种long 和 double 占用2个slot 其他占用一个 操作数栈,每个单位可以表示虚拟机支持的任何的一个数据类型 不过操作数栈其实同局部变量一样,他也是被组织一个数组, 每个元素的数据宽度和局部变量的宽度是一致的 所以对于long 和double占用2个单位长度 对于其他类型占用一个单位长度 虽然外部呈现上任何一个操作数栈可以表示任何一种数据类型,但是内部是有所区分的 如同局部变量表使用两个单位存储时,访问元素使用两个中索引小的那个类似的道理 所以可以把栈理解成线性的数组, 来一个long或者double 就分配两个单位空间作为一个元素 其余类型就分配一个单位空间作为元素 既然栈本身的结构中,线性空间的最小单位的数据宽度同局部变量, long和double占用两个 也就是下面涉及说到的数据类型的分类1 和 分类2 假设栈的示意结构如下图所示,(只是给出来一种可能每个元素的类型都可能是随机的)
左边表示呈现出来的栈元素 右边是内部的线性形式 我们当做数组好了
对栈元素的处理,显然指的是对于栈元素内部数组的处理
所以自然要分为
到底是直接复制一个单位的数据
还是直接复制两个单位的数据
一次复制占用一个单位空间 的指令 使用dup
一次复制占用两个单位空间 的指令 使用dup2
一次复制占用一个单位空间 时 假设复制的栈顶是array[0]
dup 可以理解为dup_x0
插入到他栈顶的内部线性结构的第(1+0)个元素下面 所以array[0] 对应的必然是一个完整的栈元素 ,必然是分类1 不可能是分类2的一半!
dup_x1
插入到他栈顶的内部线性结构的第(1+1)个元素下面 也就是插到第二个下面 因为array[0] 对应value1为分类1
如果接下来的是分类2的数据,必然接下来的两个单元array[1] 和array[2]是不可分割的,也就是不可能插入到array[1] 后面,所以array[1] 对应value2 也必须是分类1 也就是两个都是分类1
dup_x2
插入到他栈顶的内部线性结构的第(1+2)个元素下面 也就是插到第三个后面,array[0] 对应value1为分类1 为分类1
那么接下来的两个单位array[1] 和array[2],可以是一个分类2 也可以是两个分类1,都是可以的
dup2 可以理解为dup2_x0
插入到他栈顶的内部线性结构的第(2+0)个元素下面
这一次复制的两个单位array[0] 和 array[1], 到 array[1]下面
可能是对应value1 和value2 表示两个分类1 也可能是对应一个value1 表示类型为分类2
dup2_x1 插入到他栈顶的内部线性结构的第(2+1)个元素下面 也就是复制array[0] 和 array[1] 到第三个元素 array[2]的下面 array[0] 和 array[1] 可能分别对应value1 和value2 表示两个分类1 数据 也可能是对应着一个value1表示一个分类2数据 但是array[2] 作为第三个单位,既然能被分割,自然他必须是分类1 所以要么三个都是分类1,要么value1 分类2 value2 分类1 dup2_x2 插入到他栈顶的内部线性结构的第(2+2)个元素下面 也就是复制array[0] 和 array[1] 到第四个内部元素 array[3]的下面 一次复制两个,放到第四个下面 这种情形下的组合就非常多了 全都是分类1的数据 全部都是分类2 array[0] 和 array[1] 对应value1 表示一个分类2数据 array[2] 和 array[3] 对应value2 表示一个分类2数据 array[0] 和 array[1] 对应value1 表示一个分类2数据
array[2] 和 array[3] 对应value2 和 value3表示两个分类1数据
array[0] 和 array[1] 对应value1 和value2 表示两个分类1 数据
array[2] 和 array[3] 对应value3表示一个分类2数据
所以说只需要明确以下几点,就不难理解dup指令 操作数栈指令操作的是栈内部的存储单元,而不是以一个栈元素为单位的 long和double在栈元素内部需要两个存储单元,其余一个存储单元 两个相邻的内部单位组合起来表示一个栈元素时,是不能拆分的 再回过头看,所有的dup指令,不过是根据栈元素的实际存放的类型的排列组合,梳理出来的一些复制一个或者两个栈顶元素的实际操作方式而已 就是因为他是逆向推导的,所以看起来不好理解 |
控制转移指令
控制转移指令可以让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) |
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:volatile、static
下一篇:Redis持久化
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- Java--反射(框架设计的灵魂)案例 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash