第6章 Java虚拟机指令集
提示
来自deepseek解释
Java虚拟机指令由一个操作码(opcode)和紧随其后的零个或多个操作数(operands)组成,操作码指定要执行的操作,操作数承载要操作的值。本章详细说明每条Java虚拟机指令的格式及其执行的操作。
6.1 假设:“必须”的含义
每条指令的描述都是在满足第4章(class文件格式)静态和结构约束的Java虚拟机代码上下文中给出的。在描述单个Java虚拟机指令时,我们经常说明某种情况“必须”或“不得”成立:“value2必须是int类型。”第4章(class文件格式)的约束保证了所有这些预期实际上都会得到满足。如果在运行时某条指令描述中的某个约束(“必须”或“不得”)未被满足,则Java虚拟机的行为是未定义的。
Java虚拟机在链接时使用class文件验证器(§4.10)检查Java虚拟机代码是否满足静态和结构约束。因此,Java虚拟机只会尝试执行来自有效class文件的代码。在链接时执行验证很有吸引力,因为检查只执行一次,大大减少了运行时必须完成的工作量。只要符合《Java语言规范(Java SE 17版)》和《Java虚拟机规范(Java SE 17版)》,其他实现策略也是可行的。
6.2 保留操作码
除了本章后面指定的、在class文件(§4(class文件格式))中使用的指令操作码之外,还有三个操作码保留供Java虚拟机实现内部使用。如果将来扩展Java虚拟机的指令集,这些保留操作码保证不会被使用。
其中两个保留操作码,编号254(0xfe)和255(0xff),其助记符分别为impdep1和impdep2。这些指令旨在提供“后门”或陷阱,分别用于实现特定于软件和硬件的功能。第三个保留操作码,编号202(0xca),其助记符为breakpoint,旨在供调试器用于实现断点。
尽管这些操作码已被保留,但它们只能在Java虚拟机实现内部使用。它们不能出现在有效的class文件中。诸如调试器或JIT代码生成器(§2.13)等可能直接与已加载和执行的Java虚拟机代码交互的工具可能会遇到这些操作码。如果遇到任何这些保留指令,此类工具应尝试优雅地处理。
6.3 虚拟机错误
当内部错误或资源限制阻止Java虚拟机实现本章描述的语义时,Java虚拟机实现会抛出VirtualMachineError类的子类的实例。本规范无法预测可能在何处遇到内部错误或资源限制,也没有规定何时必须报告它们。因此,下面定义的任何VirtualMachineError子类都可以在Java虚拟机运行期间的任何时间抛出:
InternalError:Java虚拟机实现中发生了内部错误,原因可能是实现虚拟机的软件有缺陷、底层主机系统软件有缺陷或硬件有缺陷。此错误在检测到时异步(§2.10)传递,并且可能发生在程序的任何时刻。OutOfMemoryError:Java虚拟机实现已耗尽虚拟内存或物理内存,并且自动存储管理器无法回收足够的内存来满足对象创建请求。StackOverflowError:Java虚拟机实现已耗尽线程的栈空间,通常是因为执行程序中的错误导致线程进行了无限次数的递归调用。UnknownError:发生了异常或错误,但Java虚拟机实现无法报告实际的异常或错误。
6.4 指令描述的格式
本章中的Java虚拟机指令按字母顺序排列,每条指令以如下所示的条目形式表示,并各自从新的一页开始。
指令格式图中的每个单元格代表一个8位字节。指令的助记符(mnemonic)是其名称。其操作码是它的数字表示,以十进制和十六进制两种形式给出。在class文件的Java虚拟机代码中实际存在的只有数字表示。
请记住,既有编译时生成并嵌入Java虚拟机指令中的“操作数”,也有运行时计算并在操作数栈上提供的“操作数”。尽管它们来自几个不同的区域,但所有这些操作数都代表同一件事:正在执行的Java虚拟机指令要操作的值。通过隐式地从其操作数栈中获取许多操作数,而不是在编译代码中将它们显式表示为额外的操作数字节、寄存器编号等,Java虚拟机的代码保持了紧凑性。
某些指令作为相关指令家族(family)的成员呈现,共享单个描述、格式和操作数栈图。因此,一个指令家族包含多个操作码和操作码助记符;只有家族助记符出现在指令格式图中,并且一个单独的forms行列出了所有成员助记符和操作码。例如,lconst_<l>指令家族的Forms行给出了该家族中两条指令(lconst_0和lconst_1)的助记符和操作码信息:
lconst_0 = 9 (0x9)
lconst_1 = 10 (0xa)在Java虚拟机指令的描述中,指令执行对当前帧(§2.6)的操作数栈(§2.6.2)的影响以文本方式表示,栈从左到右增长,每个值单独表示。因此,
..., value1, value2 → ..., result表示一个操作,该操作开始时value2在操作数栈的顶部,value1在其下方。作为指令执行的结果,value1和value2从操作数栈弹出,并被指令计算出的结果值result替换。操作数栈的其余部分(用省略号(...)表示)不受指令执行的影响。
long和double类型的值在操作数栈上由单个条目表示。在《Java®虚拟机规范》第一版中,long和double类型的值在栈图中各由两个条目表示。
6.5 指令
aaload
操作:从数组加载引用
格式:aaload
形式:aaload = 50 (0x32)
操作数栈:
..., arrayref, index → ..., value描述:arrayref必须是reference类型,并且必须引用一个组件类型为reference的数组。index必须是int类型。arrayref和index从操作数栈弹出。数组中index位置的引用值被取出并压入操作数栈。
运行时异常:
- 如果
arrayref为null,aaload抛出NullPointerException。 - 否则,如果
index不在arrayref引用的数组边界内,aaload抛出ArrayIndexOutOfBoundsException。
aastore
操作:存储到引用数组
格式:aastore
形式:aastore = 83 (0x53)
操作数栈:
..., arrayref, index, value → ...描述:arrayref必须是reference类型,并且必须引用一个组件类型为reference的数组。index必须是int类型,value必须是reference类型。arrayref、index和value从操作数栈弹出。
如果value为null,则将其存储为数组中index位置的组件。
否则,value非空。如果value的类型与arrayref引用的数组组件类型赋值兼容,则将value存储为数组中index位置的组件。
确定非空值与数组组件类型赋值兼容性的规则如下(S为value所指对象的类型,T为数组组件的引用类型):
- 如果S是类类型:
- 如果T是类类型,则S必须与T相同,或是T的子类;
- 如果T是接口类型,则S必须实现接口T。
- 如果S是数组类型SC[](组件类型为SC的数组):
- 如果T是类类型,则T必须是
Object。 - 如果T是接口类型,则T必须是数组实现的接口之一(JLS §4.10.3)。
- 如果T是数组类型TC[],则以下之一必须成立:
- TC和SC是相同的原始类型;
- TC和SC是引用类型,且SC类型可通过这些运行时规则赋值给TC。
- 如果T是类类型,则T必须是
运行时异常:
- 如果
arrayref为null,aastore抛出NullPointerException。 - 否则,如果
index不在数组边界内,抛出ArrayIndexOutOfBoundsException。 - 否则,如果非空值的实际类型与数组组件的实际类型不兼容,抛出
ArrayStoreException。
aconst_null
操作:压入null
格式:aconst_null
形式:aconst_null = 1 (0x1)
操作数栈:
... → ..., null描述:将null对象引用压入操作数栈。
注意:Java虚拟机不强制规定null的具体值。
aload
操作:从局部变量加载引用
格式:aload index
形式:aload = 25 (0x19)
操作数栈:
... → ..., objectref描述:index是一个无符号字节,必须是当前帧(§2.6)局部变量数组的索引。index处的局部变量必须包含一个引用。该局部变量中的objectref被压入操作数栈。
注意:aload指令不能用于将returnAddress类型的值从局部变量加载到操作数栈。这种与astore指令(§astore)的不对称是有意设计的。aload操作码可与wide指令(§wide)一起使用,通过两字节无符号索引访问局部变量。
aload_<n>
操作:从局部变量加载引用
格式:aload_<n>
形式:
aload_0 = 42 (0x2a)
aload_1 = 43 (0x2b)
aload_2 = 44 (0x2c)
aload_3 = 45 (0x2d)操作数栈:
... → ..., objectref描述:<n>必须是当前帧局部变量数组的索引。<n>处的局部变量必须包含一个引用。该局部变量中的objectref被压入操作数栈。
注意:aload_<n>指令不能用于加载returnAddress类型值。每条aload_<n>指令等同于aload且索引为<n>,只是操作数<n>是隐式的。
anewarray
操作:创建新的引用数组
格式:anewarray indexbyte1 indexbyte2
形式:anewarray = 189 (0xbd)
操作数栈:
..., count → ..., arrayref描述:count必须是int类型,从操作数栈弹出。count表示要创建的数组的组件数量。无符号indexbyte1和indexbyte2用于构造当前类(§2.6)运行时常量池的索引,值为(indexbyte1 << 8) | indexbyte2。该索引处的运行时常量池项必须是对类、数组或接口类型的符号引用。命名类型被解析(§5.4.3.1)。从垃圾收集堆中分配一个长度为count、组件类型为该类型的新数组,并将引用arrayref压入操作数栈。新数组的所有组件初始化为null(引用类型的默认值)。
链接异常:解析符号引用期间可能抛出§5.4.3.1中记录的任何异常。
运行时异常:如果count小于零,抛出NegativeArraySizeException。
注意:用于创建对象引用数组的一维或多维数组的一部分。
areturn
操作:从方法返回引用
格式:areturn
形式:areturn = 176 (0xb0)
操作数栈:
..., objectref → [empty]描述:objectref必须是reference类型,且引用的对象类型必须与当前方法的返回描述符(§4.3.3)所表示的类型赋值兼容。如果当前方法是synchronized方法,则在当前线程中如同执行monitorexit指令(§monitorexit)一样更新并可能退出在方法调用时进入或重入的监视器。如果未抛出异常,objectref从当前帧(§2.6)的操作数栈弹出,并压入调用者帧的操作数栈。当前方法操作数栈上的任何其他值被丢弃。解释器随后恢复调用者的帧并将控制权返回给调用者。
运行时异常:
- 如果Java虚拟机实现不强制结构化锁定规则(§2.11.10),且当前方法是
synchronized方法,而当前线程不是方法调用时进入或重入的监视器的所有者,则抛出IllegalMonitorStateException(例如,同步方法中包含monitorexit但无monitorenter)。 - 如果实现强制结构化锁定规则且第一条规则在调用当前方法期间被违反,则抛出
IllegalMonitorStateException。
arraylength
操作:获取数组长度
格式:arraylength
形式:arraylength = 190 (0xbe)
操作数栈:
..., arrayref → ..., length描述:arrayref必须是reference类型且必须引用一个数组。它从操作数栈弹出。确定其所引用数组的长度,并将该长度作为int压入操作数栈。
运行时异常:如果arrayref为null,抛出NullPointerException。
astore
操作:将引用存储到局部变量
格式:astore index
形式:astore = 58 (0x3a)
操作数栈:
..., objectref → ...描述:index是无符号字节,必须是当前帧局部变量数组的索引。操作数栈顶的objectref必须是returnAddress或reference类型。它从操作数栈弹出,并将index处的局部变量设置为objectref。
注意:astore用于在实现finally子句时存储returnAddress类型值。aload不能加载returnAddress。可与wide配合使用。
astore_<n>
操作:将引用存储到局部变量
格式:astore_<n>
形式:
astore_0 = 75 (0x4b)
astore_1 = 76 (0x4c)
astore_2 = 77 (0x4d)
astore_3 = 78 (0x4e)操作数栈:
..., objectref → ...描述:<n>必须是局部变量数组索引。栈顶的objectref必须是returnAddress或reference类型,弹出后设置局部变量<n>。
注意:每条astore_<n>等同于astore且索引为<n>,操作数隐式。
athrow
操作:抛出异常或错误
格式:athrow
形式:athrow = 191 (0xbf)
操作数栈:
..., objectref → objectref描述:objectref必须是reference类型,且引用的对象必须是Throwable或其子类的实例。它从操作数栈弹出,然后通过在当前方法(§2.6)中搜索与objectref类匹配的第一个异常处理器来抛出(算法见§2.10)。如果找到匹配的处理器,则重置PC到该位置,清除当前帧的操作数栈,将objectref压回栈,继续执行。如果当前帧中未找到匹配处理器,则弹出该帧。如果当前帧代表同步方法的调用,则如同执行monitorexit退出监视器。最后恢复调用者帧(如果存在)并重新抛出objectref;若不存在,则当前线程退出。
运行时异常:
- 如果
objectref为null,抛出NullPointerException(而不是objectref)。 - 如果实现不强制结构化锁定且当前方法为同步方法且线程不是监视器所有者,则抛出
IllegalMonitorStateException(替代原抛出对象)。 - 如果实现强制结构化锁定且第一条规则违反,同样抛出
IllegalMonitorStateException。
注意:栈图可能误导:若在当前方法中匹配到处理器,athrow丢弃所有栈值并压入抛出对象;若未匹配,则栈被清空,对象压入处理方法的空栈。
baload
操作:从数组加载byte或boolean
格式:baload
形式:baload = 51 (0x33)
操作数栈:
..., arrayref, index → ..., value描述:arrayref必须引用组件类型为byte或boolean的数组。index为int。弹出两者,取出组件值,符号扩展为int并压栈。
运行时异常:同aaload。
bastore
操作:存储到byte或boolean数组
格式:bastore
形式:bastore = 84 (0x54)
操作数栈:
..., arrayref, index, value → ...描述:arrayref引用byte或boolean数组,index和value为int。弹出三者。若为byte数组,value截断为byte存储;若为boolean数组,将value与1按位与后存储。
运行时异常:同aastore。
bipush
操作:压入byte
格式:bipush byte
形式:bipush = 16 (0x10)
操作数栈:
... → ..., value描述:立即byte符号扩展为int值并压栈。
caload
操作:从数组加载char
格式:caload
形式:caload = 52 (0x34)
操作数栈:
..., arrayref, index → ..., value描述:arrayref引用char数组,index为int。取出组件值,零扩展为int并压栈。
运行时异常:同aaload。
castore
操作:存储到char数组
格式:castore
形式:castore = 85 (0x55)
操作数栈:
..., arrayref, index, value → ...描述:arrayref引用char数组,index和value为int。value截断为char存储。
运行时异常:同aastore。
checkcast
操作:检查对象是否为给定类型
格式:checkcast indexbyte1 indexbyte2
形式:checkcast = 192 (0xc0)
操作数栈:
..., objectref → ..., objectref描述:objectref为reference。构造常量池索引,解析类型。若objectref为null,栈不变;否则,若可转换为解析类型,栈不变;否则抛出ClassCastException。转换规则与instanceof相同。
链接异常:解析期间可抛出§5.4.3.1中的异常。
运行时异常:若不能转换,抛出ClassCastException。
注意:与instanceof类似,但处理null和失败行为不同。
d2f
操作:将double转换为float
格式:d2f
形式:d2f = 144 (0x90)
操作数栈:
..., value → ..., result描述:栈顶value为double,弹出并使用向最近舍入(§2.8)转换为float结果压栈。太小则转为同号零,太大转为同号无穷大,double NaN转为float NaN。
注意:窄化转换,可能丢失精度。
d2i
操作:将double转换为int
格式:d2i
形式:d2i = 142 (0x8e)
操作数栈:
..., value → ..., result描述:栈顶double弹出,转换为int:
- 若为NaN,结果为0;
- 否则若非无穷,使用向零舍入得到整数值V,若可表示为
int则结果为V; - 否则太小或太负,结果为
int的最小或最大值。
注意:窄化转换,可能丢失精度和符号。
d2l
操作:将double转换为long
格式:d2l
形式:d2l = 143 (0x8f)
操作数栈:
..., value → ..., result描述:类似d2i,但结果为long。
dadd
操作:double加法
格式:dadd
形式:dadd = 99 (0x63)
操作数栈:
..., value1, value2 → ..., result描述:弹出两个double,计算value1 + value2,结果压栈。遵循IEEE 754规则(NaN、无穷、零、溢出、下溢等)。支持渐进下溢。从不抛出运行时异常。
daload
操作:从数组加载double
格式:daload
形式:daload = 49 (0x31)
操作数栈:
..., arrayref, index → ..., value描述:arrayref引用double数组,index为int。取出double值压栈。
运行时异常:同aaload。
dastore
操作:存储到double数组
格式:dastore
形式:dastore = 82 (0x52)
操作数栈:
..., arrayref, index, value → ...描述:arrayref引用double数组,index和value为double。存储value。
运行时异常:同aastore。
dcmp<op>
操作:比较double
格式:dcmp<op>
形式:
dcmpg = 152 (0x98)
dcmpl = 151 (0x97)操作数栈:
..., value1, value2 → ..., result描述:弹出两个double,比较:
- 若
value1 > value2,压入1; - 若相等,压入0;
- 若
value1 < value2,压入-1; - 若任一为NaN,
dcmpg压入1,dcmpl压入-1。
遵循IEEE 754有序比较。
注意:两者仅在NaN处理上不同。
dconst_<d>
操作:压入double常量
格式:dconst_<d>
形式:
dconst_0 = 14 (0xe)
dconst_1 = 15 (0xf)操作数栈:
... → ..., <d>描述:压入0.0或1.0。
ddiv
操作:double除法
格式:ddiv
形式:ddiv = 111 (0x6f)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 / value2,遵循IEEE 754(NaN、无穷、零、符号规则)。支持渐进下溢。从不抛出运行时异常。
dload
操作:从局部变量加载double
格式:dload index
形式:dload = 24 (0x18)
操作数栈:
... → ..., value描述:index和index+1必须是有效索引。局部变量index处为double,将其值压栈。
注意:可与wide配合。
dload_<n>
操作:从局部变量加载double
格式:dload_<n>
形式:
dload_0 = 38 (0x26)
dload_1 = 39 (0x27)
dload_2 = 40 (0x28)
dload_3 = 41 (0x29)操作数栈:
... → ..., value描述:<n>和<n>+1为索引,加载double值。
dmul
操作:double乘法
格式:dmul
形式:dmul = 107 (0x6b)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 * value2,遵循IEEE 754。支持渐进下溢,从不抛出异常。
dneg
操作:double取反
格式:dneg
形式:dneg = 119 (0x77)
操作数栈:
..., value → ..., result描述:弹出double,计算算术取反。对于NaN,结果仍为NaN(不反转符号位)。无穷和零取反改变符号。
注意:未采用IEEE 754 2019版要求反转NaN符号位的更强要求。
drem
操作:double取余
格式:drem
形式:drem = 115 (0x73)
操作数栈:
..., value1, value2 → ..., result描述:计算余数,不同于IEEE 754余数(因舍入策略不同)。Java虚拟机定义drem行为类似于整数余数,隐含使用向零舍入的除法(类似C的fmod)。规则:
- NaN则NaN;
- 符号与被除数相同;
- 被除数为无穷或除数为零则NaN;
- 被除数有限除数无穷则结果等于被除数;
- 被除数为零除数有限则结果等于被除数;
- 否则
result = value1 - (value2 * q),其中q为整数,符号与value1/value2相同,且其绝对值尽可能大而不超过真实商。 - 从不抛出异常(除零不会抛出)。
注意:IEEE 754余数可通过Math.IEEEremainder计算。
dreturn
操作:从方法返回double
格式:dreturn
形式:dreturn = 175 (0xaf)
操作数栈:
..., value → [empty]描述:当前方法返回类型为double,弹出value,压入调用者栈。同步方法处理监视器。
运行时异常:同areturn中的结构化锁定异常。
dstore
操作:将double存储到局部变量
格式:dstore index
形式:dstore = 57 (0x39)
操作数栈:
..., value → ...描述:index和index+1为索引,栈顶double弹出,存储到index和index+1。
注意:可与wide配合。
dstore_<n>
操作:将double存储到局部变量
格式:dstore_<n>
形式:
dstore_0 = 71 (0x47)
dstore_1 = 72 (0x48)
dstore_2 = 73 (0x49)
dstore_3 = 74 (0x4a)操作数栈:
..., value → ...描述:<n>和<n>+1索引,存储double。
dsub
操作:double减法
格式:dsub
形式:dsub = 103 (0x67)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 - value2。遵循IEEE 754,a-b等同于a+(-b),但0.0 - x不等于-x(正零情况)。支持渐进下溢,从不抛出异常。
dup
操作:复制栈顶值
格式:dup
形式:dup = 89 (0x59)
操作数栈:
..., value → ..., value, value描述:复制栈顶值(类别1计算类型)并压入。
dup_x1
操作:复制栈顶值并插入到两个值之下
格式:dup_x1
形式:dup_x1 = 90 (0x5a)
操作数栈:
..., value2, value1 → ..., value1, value2, value1描述:复制栈顶value1,插入到value2之下。两个值都必须是类别1类型。
dup_x2
操作:复制栈顶值并插入到两个或三个值之下
格式:dup_x2
形式:dup_x2 = 91 (0x5b)
操作数栈:
- 形式1:
..., value3, value2, value1 → ..., value1, value3, value2, value1(全部类别1) - 形式2:
..., value2, value1 → ..., value1, value2, value1(value1类别1,value2类别2)
描述:复制栈顶value1,插入到下面两个或三个值之下,取决于下面值的类别。
dup2
操作:复制栈顶一个或两个值
格式:dup2
形式:dup2 = 92 (0x5c)
操作数栈:
- 形式1:
..., value2, value1 → ..., value2, value1, value2, value1(两个类别1) - 形式2:
..., value → ..., value, value(一个类别2)
描述:复制栈顶一个类别2值或两个类别1值,按原顺序压入。
dup2_x1
操作:复制栈顶一个或两个值并插入到两个或三个值之下
格式:dup2_x1
形式:dup2_x1 = 93 (0x5d)
操作数栈:
- 形式1:
..., value3, value2, value1 → ..., value2, value1, value3, value2, value1(全部类别1) - 形式2:
..., value2, value1 → ..., value1, value2, value1(value1类别2,value2类别1)
描述:复制栈顶一个或两个值(value2, value1或单个value1),按原顺序插入到下方。
dup2_x2
操作:复制栈顶一个或两个值并插入到两个、三个或四个值之下
格式:dup2_x2
形式:dup2_x2 = 94 (0x5e)
操作数栈:
- 形式1:
..., value4, value3, value2, value1 → ..., value2, value1, value4, value3, value2, value1(四个类别1) - 形式2:
..., value3, value2, value1 → ..., value1, value3, value2, value1(value1类别2,value2和value3类别1) - 形式3:
..., value3, value2, value1 → ..., value2, value1, value3, value2, value1(value1和value2类别1,value3类别2) - 形式4:
..., value2, value1 → ..., value1, value2, value1(两个类别2)
描述:复制栈顶一个或两个值(value2, value1或单个value1),按原顺序插入到下方适当位置,具体取决于栈中其他值的类别。
f2d
操作:将float转换为double
格式:f2d
形式:f2d = 141 (0x8d)
操作数栈:
..., value → ..., result描述:栈顶的value必须是float类型。它被弹出并转换为double结果,结果压入栈。
注意:f2d执行拓宽原始转换(JLS §5.1.2)。
f2i
操作:将float转换为int
格式:f2i
形式:f2i = 139 (0x8b)
操作数栈:
..., value → ..., result描述:栈顶的value必须是float类型。它被弹出并转换为int结果,结果压入栈:
- 如果
value是NaN,结果为int0。 - 否则,如果
value不是无穷大,则使用向零舍入策略(§2.8)舍入为整数值V。如果V可表示为int,则结果为V。 - 否则,
value太小(负大值或负无穷)则结果为int的最小值;太大(正大值或正无穷)则结果为int的最大值。
注意:执行窄化原始转换(JLS §5.1.3),可能丢失幅值和精度。
f2l
操作:将float转换为long
格式:f2l
形式:f2l = 140 (0x8c)
操作数栈:
..., value → ..., result描述:类似f2i,但结果为long。NaN为0,向零舍入,超出范围则取long的最小或最大值。
注意:窄化原始转换。
fadd
操作:float加法
格式:fadd
形式:fadd = 98 (0x62)
操作数栈:
..., value1, value2 → ..., result描述:弹出两个float,计算value1 + value2,结果压栈。遵循IEEE 754规则(NaN、无穷、零、溢出、下溢)。支持渐进下溢。从不抛出运行时异常。
faload
操作:从数组加载float
格式:faload
形式:faload = 48 (0x30)
操作数栈:
..., arrayref, index → ..., value描述:arrayref必须引用组件类型为float的数组,index为int。取出float值压栈。
运行时异常:同aaload。
fastore
操作:存储到float数组
格式:fastore
形式:fastore = 81 (0x51)
操作数栈:
..., arrayref, index, value → ...描述:arrayref引用float数组,index为int,value为float。存储。
运行时异常:同aastore。
fcmp<op>
操作:比较float
格式:fcmp<op>
形式:
fcmpg = 150 (0x96)
fcmpl = 149 (0x95)操作数栈:
..., value1, value2 → ..., result描述:弹出两个float进行比较:
- 若
value1 > value2,压入1; - 若相等,压入0;
- 若
value1 < value2,压入-1; - 若任一为NaN,
fcmpg压入1,fcmpl压入-1。
遵循IEEE 754有序比较。
注意:两者仅在NaN处理上不同。
fconst_<f>
操作:压入float常量
格式:fconst_<f>
形式:
fconst_0 = 11 (0xb)
fconst_1 = 12 (0xc)
fconst_2 = 13 (0xd)操作数栈:
... → ..., <f>描述:压入0.0、1.0或2.0。
fdiv
操作:float除法
格式:fdiv
形式:fdiv = 110 (0x6e)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 / value2,遵循IEEE 754。支持渐进下溢,从不抛出运行时异常。
fload
操作:从局部变量加载float
格式:fload index
形式:fload = 23 (0x17)
操作数栈:
... → ..., value描述:index为局部变量索引,加载float值压栈。
注意:可与wide配合。
fload_<n>
操作:从局部变量加载float
格式:fload_<n>
形式:
fload_0 = 34 (0x22)
fload_1 = 35 (0x23)
fload_2 = 36 (0x24)
fload_3 = 37 (0x25)操作数栈:
... → ..., value描述:<n>为索引,加载float。
fmul
操作:float乘法
格式:fmul
形式:fmul = 106 (0x6a)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 * value2,遵循IEEE 754。支持渐进下溢,从不抛出异常。
fneg
操作:float取反
格式:fneg
形式:fneg = 118 (0x76)
操作数栈:
..., value → ..., result描述:弹出float,计算算术取反。NaN仍为NaN(不反转符号位)。无穷和零改变符号。
注意:未采用IEEE 754 2019版要求反转NaN符号位的更强要求。
frem
操作:float取余
格式:frem
形式:frem = 114 (0x72)
操作数栈:
..., value1, value2 → ..., result描述:计算余数,类似drem,但针对float。使用向零舍入的隐含除法(类似fmod)。规则同drem。从不抛出异常。
注意:IEEE 754余数可通过Math.IEEEremainder计算。
freturn
操作:从方法返回float
格式:freturn
形式:freturn = 174 (0xae)
操作数栈:
..., value → [empty]描述:当前方法返回类型为float,弹出value压入调用者栈。同步方法处理监视器。
运行时异常:同areturn中的结构化锁定异常。
fstore
操作:将float存储到局部变量
格式:fstore index
形式:fstore = 56 (0x38)
操作数栈:
..., value → ...描述:index为局部变量索引,弹出栈顶float,存储到index。
注意:可与wide配合。
fstore_<n>
操作:将float存储到局部变量
格式:fstore_<n>
形式:
fstore_0 = 67 (0x43)
fstore_1 = 68 (0x44)
fstore_2 = 69 (0x45)
fstore_3 = 70 (0x46)操作数栈:
..., value → ...描述:<n>为索引,存储float。
fsub
操作:float减法
格式:fsub
形式:fsub = 102 (0x66)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 - value2。遵循IEEE 754,a-b等同于a+(-b),但0.0 - x不等于-x。支持渐进下溢,从不抛出异常。
getfield
操作:从对象中获取字段
格式:getfield indexbyte1 indexbyte2
形式:getfield = 180 (0xb4)
操作数栈:
..., objectref → ..., value描述:构造常量池索引,解析字段引用(§5.4.3.2)。弹出objectref(非数组引用),取出字段值压栈。
链接异常:字段解析可能抛出§5.4.3.2中的错误;若解析字段是静态字段,抛出IncompatibleClassChangeError。
运行时异常:若objectref为null,抛出NullPointerException。
注意:不能用于数组的length字段,应使用arraylength。
getstatic
操作:从类获取静态字段
格式:getstatic indexbyte1 indexbyte2
形式:getstatic = 178 (0xb2)
操作数栈:
... → ..., value描述:构造常量池索引,解析字段引用。成功解析后,初始化声明该字段的类或接口(若未初始化)。获取字段值压栈。
链接异常:字段解析错误;若解析字段不是静态字段,抛出IncompatibleClassChangeError。
运行时异常:若初始化导致错误,可能抛出Error(详见§5.5)。
goto
操作:无条件跳转
格式:goto branchbyte1 branchbyte2
形式:goto = 167 (0xa7)
操作数栈:无变化
描述:构造16位有符号偏移(branchbyte1 << 8) | branchbyte2,从当前指令地址跳转,目标必须为方法内指令操作码地址。
goto_w
操作:无条件跳转(宽索引)
格式:goto_w branchbyte1 branchbyte2 branchbyte3 branchbyte4
形式:goto_w = 200 (0xc8)
操作数栈:无变化
描述:构造32位有符号偏移,跳转。目标必须为方法内指令。
注意:虽然偏移为4字节,但方法大小受限于65535字节(§4.11)。
i2b
操作:将int转换为byte
格式:i2b
形式:i2b = 145 (0x91)
操作数栈:
..., value → ..., result描述:弹出int,截断为byte,符号扩展为int压栈。
注意:窄化转换,可能丢失信息。
i2c
操作:将int转换为char
格式:i2c
形式:i2c = 146 (0x92)
操作数栈:
..., value → ..., result描述:弹出int,截断为char,零扩展为int压栈。
i2d
操作:将int转换为double
格式:i2d
形式:i2d = 135 (0x87)
操作数栈:
..., value → ..., result描述:弹出int,转换为double(精确,因为所有int值可精确表示)。
注意:拓宽转换。
i2f
操作:将int转换为float
格式:i2f
形式:i2f = 134 (0x86)
操作数栈:
..., value → ..., result描述:弹出int,使用向最近舍入转换为float,可能丢失精度(float仅有24位有效数)。
注意:拓宽转换但可能损失精度。
i2l
操作:将int转换为long
格式:i2l
形式:i2l = 133 (0x85)
操作数栈:
..., value → ..., result描述:弹出int,符号扩展为long(精确)。
注意:拓宽转换。
i2s
操作:将int转换为short
格式:i2s
形式:i2s = 147 (0x93)
操作数栈:
..., value → ..., result描述:弹出int,截断为short,符号扩展为int。
注意:窄化转换。
iadd
操作:int加法
格式:iadd
形式:iadd = 96 (0x60)
操作数栈:
..., value1, value2 → ..., result描述:弹出两个int,计算和,取低32位作为结果。溢出时结果符号可能与数学和不同,但不抛出异常。
iaload
操作:从数组加载int
格式:iaload
形式:iaload = 46 (0x2e)
操作数栈:
..., arrayref, index → ..., value描述:arrayref引用int数组,index为int,取出int值压栈。
运行时异常:同aaload。
iand
操作:int按位与
格式:iand
形式:iand = 126 (0x7e)
操作数栈:
..., value1, value2 → ..., result描述:弹出两个int,按位与后压栈。
iastore
操作:存储到int数组
格式:iastore
形式:iastore = 79 (0x4f)
操作数栈:
..., arrayref, index, value → ...描述:arrayref引用int数组,index和value为int,存储。
运行时异常:同aastore。
iconst_<i>
操作:压入int常量
格式:iconst_<i>
形式:
iconst_m1 = 2 (0x2)
iconst_0 = 3 (0x3)
iconst_1 = 4 (0x4)
iconst_2 = 5 (0x5)
iconst_3 = 6 (0x6)
iconst_4 = 7 (0x7)
iconst_5 = 8 (0x8)操作数栈:
... → ..., <i>描述:压入-1、0、1、2、3、4或5。
idiv
操作:int除法
格式:idiv
形式:idiv = 108 (0x6c)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 / value2,向零舍入。特殊:若被除数为int最小值且除数为-1,则结果为被除数(溢出但不抛异常)。
运行时异常:若除数为0,抛出ArithmeticException。
if_acmp<cond>
操作:引用比较成功则跳转
格式:if_acmp<cond> branchbyte1 branchbyte2
形式:
if_acmpeq = 165 (0xa5)
if_acmpne = 166 (0xa6)操作数栈:
..., value1, value2 → ...描述:弹出两个reference比较,if_acmpeq在相等时跳转,if_acmpne在不相等时跳转。构造16位偏移,跳转到目标;否则执行下一条。
if_icmp<cond>
操作:int比较成功则跳转
格式:if_icmp<cond> branchbyte1 branchbyte2
形式:
if_icmpeq = 159 (0x9f)
if_icmpne = 160 (0xa0)
if_icmplt = 161 (0xa1)
if_icmpge = 162 (0xa2)
if_icmpgt = 163 (0xa3)
if_icmple = 164 (0xa4)操作数栈:
..., value1, value2 → ...描述:弹出两个int,根据条件(==、≠、<、≤、>、≥)跳转。
if<cond>
操作:int与零比较成功则跳转
格式:if<cond> branchbyte1 branchbyte2
形式:
ifeq = 153 (0x99)
ifne = 154 (0x9a)
iflt = 155 (0x9b)
ifge = 156 (0x9c)
ifgt = 157 (0x9d)
ifle = 158 (0x9e)操作数栈:
..., value → ...描述:弹出int,与零比较(==、≠、<、≥、>、≤),满足则跳转。
ifnonnull
操作:引用非空则跳转
格式:ifnonnull branchbyte1 branchbyte2
形式:ifnonnull = 199 (0xc7)
操作数栈:
..., value → ...描述:弹出reference,若非null则跳转。
ifnull
操作:引用为空则跳转
格式:ifnull branchbyte1 branchbyte2
形式:ifnull = 198 (0xc6)
操作数栈:
..., value → ...描述:弹出reference,若为null则跳转。
iinc
操作:局部变量增加常量
格式:iinc index const
形式:iinc = 132 (0x84)
操作数栈:无变化
描述:index为局部变量索引,const为有符号字节,扩展为int,将局部变量int值增加该量。
注意:可与wide配合使用两字节索引和两字节增量。
iload
操作:从局部变量加载int
格式:iload index
形式:iload = 21 (0x15)
操作数栈:
... → ..., value描述:加载int局部变量。
注意:可与wide配合。
iload_<n>
操作:从局部变量加载int
格式:iload_<n>
形式:
iload_0 = 26 (0x1a)
iload_1 = 27 (0x1b)
iload_2 = 28 (0x1c)
iload_3 = 29 (0x1d)操作数栈:
... → ..., value描述:<n>为索引,加载int。
imul
操作:int乘法
格式:imul
形式:imul = 104 (0x68)
操作数栈:
..., value1, value2 → ..., result描述:计算value1 * value2,取低32位。溢出不抛异常。
ineg
操作:int取反
格式:ineg
形式:ineg = 116 (0x74)
操作数栈:
..., value → ..., result描述:计算-value。对最大负数取反仍为自身(溢出不抛异常)。
instanceof
操作:确定对象是否为给定类型
格式:instanceof indexbyte1 indexbyte2
形式:instanceof = 193 (0xc1)
操作数栈:
..., objectref → ..., result描述:弹出objectref,解析类型。若为null压入0;否则若是指定类型的实例压入1,否则0。转换规则与checkcast相同。
链接异常:解析异常。
注意:与checkcast区别在于处理null和失败行为。
invokedynamic
操作:调用动态计算调用点
格式:invokedynamic indexbyte1 indexbyte2 0 0
形式:invokedynamic = 186 (0xba)
操作数栈:
..., [arg1, [arg2 ...]] → ...描述:解析动态调用点符号引用(§5.4.3.6)得到CallSite,绑定到该指令。然后调用目标方法句柄,如同invokevirtual调用MethodHandle.invokeExact。
链接异常:解析期间异常。
注意:若解析成功,则目标非空且方法类型匹配,不会抛出NullPointerException或WrongMethodTypeException。
invokeinterface
操作:调用接口方法
格式:invokeinterface indexbyte1 indexbyte2 count 0
形式:invokeinterface = 185 (0xb9)
操作数栈:
..., objectref, [arg1, [arg2 ...]] → ...描述:解析接口方法引用(§5.4.3.4)。count表示参数大小(必须非零)。根据objectref的类选择方法(§5.4.6)。如果是同步方法则进入监视器。非本地方法创建新帧传递参数;本地方法绑定并调用。
链接异常:解析错误;若解析方法为静态,抛出IncompatibleClassChangeError。
运行时异常:
objectref为null →NullPointerException。- 类未实现接口 →
IncompatibleClassChangeError。 - 选择的方法不是public或private →
IllegalAccessError。 - 选择抽象方法 →
AbstractMethodError。 - 本地方法无法绑定 →
UnsatisfiedLinkError。 - 多个最具体非抽象方法 →
IncompatibleClassChangeError。 - 无非抽象最具体方法 →
AbstractMethodError。
注意:count冗余,历史原因。第四字节保留。
invokespecial
操作:调用实例方法;直接调用实例初始化方法及当前类及其超类型的方法
格式:invokespecial indexbyte1 indexbyte2
形式:invokespecial = 183 (0xb7)
操作数栈:
..., objectref, [arg1, [arg2 ...]] → ...描述:解析方法引用。若解析方法不是<init>且符号引用命名的是当前类的超类且ACC_SUPER标志设置,则C为当前类的直接超类;否则C为符号引用的类。查找实际方法:在C中查找,若找不到且在类中则沿超类链查找,若在接口中则检查Object的public方法,最后检查最具体超接口方法。调用过程与invokeinterface类似。
链接异常:解析错误;若解析为静态方法抛出IncompatibleClassChangeError;若为<init>且类与符号引用不同则NoSuchMethodError。
运行时异常:objectref为null → NullPointerException;选择抽象方法 → AbstractMethodError;本地无法绑定 → UnsatisfiedLinkError;多个/无最具体非抽象 → IncompatibleClassChangeError/AbstractMethodError。
注意:invokespecial用于调用<init>、super方法等。历史上称invokenonvirtual。
invokestatic
操作:调用类(静态)方法
格式:invokestatic indexbyte1 indexbyte2
形式:invokestatic = 184 (0xb8)
操作数栈:
..., [arg1, [arg2 ...]] → ...描述:解析静态方法引用(§5.4.3.3)。解析后初始化声明类。参数从栈弹出,创建帧或调用本地方法。
链接异常:解析错误;若解析为实例方法抛出IncompatibleClassChangeError。
运行时异常:初始化可能抛出Error;本地无法绑定 → UnsatisfiedLinkError。