JVM 字节码指令集非常庞大,包含了大量的指令来完成各种操作。 我们只需要掌握一些常用的字节码指令即可。
以下是一些最常用、最核心的 JVM 字节码指令,按照功能类别进行划分:
1. 操作数栈 (Operand Stack) 操作指令:
nop
(No Operation): 什么也不做,空指令,通常用于调试或占位。aconst_null
(Push null): 将null
引用值推送到操作数栈顶。iconst_m1
,iconst_0
,iconst_1
,iconst_2
,iconst_3
,iconst_4
,iconst_5
(Push int constant): 将int
型常量-1, 0, 1, 2, 3, 4, 5
推送到操作数栈顶。fconst_0
,fconst_1
,fconst_2
(Push float constant): 将float
型常量0.0f, 1.0f, 2.0f
推送到操作数栈顶。dconst_0
,dconst_1
(Push double constant): 将double
型常量0.0d, 1.0d
推送到操作数栈顶。lconst_0
,lconst_1
(Push long constant): 将long
型常量0L, 1L
推送到操作数栈顶。bipush <b>
(Push byte): 将byte
型常量<b>
推送到操作数栈顶。sipush <s
> (Push short): 将short
型常量<s
> 推送到操作数栈顶。ldc <index>
(Load constant): 从常量池中加载int
,float
,String
类型常量或类引用推送到操作数栈顶。ldc_w <index>
(Load constant wide): 宽索引版本ldc
,用于加载常量池索引范围更大的常量。ldc2_w <index>
(Load constant wide 2): 从常量池中加载long
或double
类型常量推送到操作数栈顶 (占用两个栈槽)。pop
(Pop top operand stack value): 将栈顶数值弹出 (通常用于丢弃不需要的计算结果)。pop2
(Pop top 2 operand stack values): 将栈顶两个数值弹出 (用于long
和double
类型,或两个非long
和double
类型的值)。dup
(Duplicate top operand stack value): 复制栈顶数值,并将副本压入栈顶。dup_x1
(Duplicate top operand stack value and insert below): 复制栈顶数值,并将副本插入到栈顶数值之下。dup_x2
(Duplicate top operand stack value and insert two values down): 复制栈顶数值,并将副本插入到栈顶数值之下两个位置。dup2
(Duplicate top 1 or 2 operand stack values): 复制栈顶一个或两个数值,并将副本压入栈顶 (用于long
和double
或两个非long
和double
类型的值)。dup2_x1
(Duplicate top 1 or 2 operand stack values and insert below): 复制栈顶一个或两个数值,并将副本插入到栈顶数值之下。dup2_x2
(Duplicate top 1 or 2 operand stack values and insert two values down): 复制栈顶一个或两个数值,并将副本插入到栈顶数值之下两个位置。swap
(Swap top two operand stack values): 交换栈顶两个数值。
2. 局部变量表 (Local Variable Table) 访问指令:
iload <index>
,fload <index>
,aload <index>
,lload <index>
,dload <index>
(Load from local variable): 将指定索引的int
,float
,reference
,long
,double
型局部变量加载到操作数栈顶。iload_0
,iload_1
,iload_2
,iload_3
(Load int from local variable 0-3): 加载索引为0, 1, 2, 3
的int
型局部变量到栈顶 (更高效的指令)。fload_0
,fload_1
,fload_2
,fload_3
(Load float from local variable 0-3): 加载索引为0, 1, 2, 3
的float
型局部变量到栈顶。aload_0
,aload_1
,aload_2
,aload_3
(Load reference from local variable 0-3): 加载索引为0, 1, 2, 3
的reference
型局部变量到栈顶 (例如this
引用通常在索引 0)。lload_0
,lload_1
,lload_2
,lload_3
(Load long from local variable 0-3): 加载索引为0, 1, 2, 3
的long
型局部变量到栈顶。dload_0
,dload_1
,dload_2
,dload_3
(Load double from local variable 0-3): 加载索引为0, 1, 2, 3
的double
型局部变量到栈顶。istore <index>
,fstore <index>
,astore <index>
,lstore <index>
,dstore <index>
(Store into local variable): 将栈顶的int
,float
,reference
,long
,double
型数值存储到指定索引的局部变量。istore_0
,istore_1
,istore_2
,istore_3
(Store int into local variable 0-3): 将栈顶int
型数值存储到索引为0, 1, 2, 3
的局部变量 (更高效的指令)。fstore_0
,fstore_1
,fstore_2
,fstore_3
(Store float into local variable 0-3): 将栈顶float
型数值存储到索引为0, 1, 2, 3
的局部变量。astore_0
,astore_1
,astore_2
,astore_3
(Store reference into local variable 0-3): 将栈顶reference
型数值存储到索引为0, 1, 2, 3
的局部变量。lstore_0
,lstore_1
,lstore_2
,lstore_3
(Store long into local variable 0-3): 将栈顶long
型数值存储到索引为0, 1, 2, 3
的局部变量。dstore_0
,dstore_1
,dstore_2
,dstore_3
(Store double into local variable 0-3): 将栈顶double
型数值存储到索引为0, 1, 2, 3
的局部变量。
3. 运算指令:
- 算术运算:
iadd
,fadd
,dadd
,ladd
(加法)isub
,fsub
,dsub
,lsub
(减法)imul
,fmul
,dmul
,lmul
(乘法)idiv
,fdiv
,ddiv
,ldiv
(除法)irem
,frem
,drem
,lrem
(求余数)ineg
,fneg
,dneg
,lneg
(取反)ishl
,lshl
(左移)ishr
,lshr
(算术右移)iushr
,lushr
(逻辑右移)iand
,land
(按位与)ior
,lor
(按位或)ixor
,lxor
(按位异或)iinc <index> <const>
(int 变量自增,局部变量表操作)
- 类型转换:
i2b
,i2c
,i2s
(int 转 byte, char, short)i2l
,i2f
,i2d
(int 转 long, float, double)l2i
,l2f
,l2d
(long 转 int, float, double)f2i
,f2l
,f2d
(float 转 int, long, double)d2i
,d2l
,d2f
(double 转 int, long, float)
- 比较:
lcmp
(long 比较)fcmpl
,fcmpg
(float 比较,l
表示 NaN 时结果为 -1,g
表示 NaN 时结果为 1)dcmpl
,dcmpg
(double 比较,l
表示 NaN 时结果为 -1,g
表示 NaN 时结果为 1)
4. 类型转换和检查指令:
checkcast <class>
(Check whether object is of given type): 检查对象是否是指定类型,如果是则继续,否则抛出ClassCastException
。instanceof <class>
(Determine if object is of given type): 检查对象是否是指定类型的实例,结果 (1
或0
) 推送到操作数栈顶。
5. 对象操作指令:
new <class>
(Create new object): 创建一个对象,但不初始化,只在堆上分配空间,并将未初始化的对象引用推送到操作数栈顶。anewarray <component type>
(Create new array of references): 创建一个指定组件类型的引用类型数组,数组长度从操作数栈弹出。newarray <atype>
(Create new array of primitive type): 创建一个指定基本类型的基本类型数组,数组长度从操作数栈弹出 (例如int
,boolean
,char
等)。arraylength
(Get length of array): 获取数组长度,数组引用从操作数栈弹出,数组长度推送到栈顶。aaload
,baload
,caload
,saload
,iaload
,laload
,faload
,daload
(Load from array): 从数组中加载元素,数组引用和索引从操作数栈弹出,加载的元素值推送到栈顶 (对应reference
,byte
,char
,short
,int
,long
,float
,double
类型)。aastore
,bastore
,castore
,sastore
,iastore
,lastore
,fastore
,dastore
(Store into array): 将值存储到数组中,数组引用、索引和要存储的值从操作数栈弹出 (对应reference
,byte
,char
,short
,int
,long
,float
,double
类型)。getfield <field>
(Fetch field from object): 获取对象的实例字段的值,对象引用从操作数栈弹出,字段值推送到栈顶。putfield <field>
(Set field in object): 设置对象的实例字段的值,对象引用和要设置的字段值从操作数栈弹出。getstatic <field>
(Get static field from class): 获取类的静态字段的值,字段值推送到操作数栈顶。putstatic <field>
(Set static field in class): 设置类的静态字段的值,要设置的字段值从操作数栈弹出。
6. 方法调用和返回指令:
invokevirtual <method>
(Invoke virtual method): 调用对象的虚方法 (运行时多态)。invokespecial <method>
(Invoke instance method; special handling): 调用实例方法,但需要特殊处理的情况,例如:- 调用父类方法 (
super.method()
) - 调用私有方法
- 调用构造器 (
<init>
)
- 调用父类方法 (
invokestatic <method>
(Invoke a class (static) method): 调用静态方法。invokeinterface <interface method>
(Invoke interface method): 调用接口方法。invokedynamic <method>
(Invoke dynamic method): 用于支持动态语言特性,例如 Lambda 表达式、动态代理等 (JDK 7 新增)。ireturn
,freturn
,areturn
,lreturn
,dreturn
(Return from method): 从方法返回,并将栈顶的int
,float
,reference
,long
,double
类型值作为返回值返回。return
(Return void from method): 从void
方法返回。
7. 控制流指令:
- 条件跳转:
ifeq <branchoffset>
(If int compare with zero is equal branch): 如果栈顶int
型数值等于 0,则跳转。ifne <branchoffset>
(If int compare with zero is not equal branch): 如果栈顶int
型数值不等于 0,则跳转。iflt <branchoffset>
(If int compare with zero is less than branch): 如果栈顶int
型数值小于 0,则跳转。ifge <branchoffset>
(If int compare with zero is greater than or equal branch): 如果栈顶int
型数值大于等于 0,则跳转。ifgt <branchoffset>
(If int compare with zero is greater than branch): 如果栈顶int
型数值大于 0,则跳转。ifle <branchoffset>
(If int compare with zero is less than or equal branch): 如果栈顶int
型数值小于等于 0,则跳转。if_icmpeq <branchoffset>
(If int compare equal branch): 如果栈顶两个int
型数值相等,则跳转。if_icmpne <branchoffset>
(If int compare not equal branch): 如果栈顶两个int
型数值不相等,则跳转。if_icmplt <branchoffset>
(If int compare less than branch): 如果栈顶第一个int
型数值小于第二个,则跳转。if_icmpge <branchoffset>
(If int compare greater than or equal branch): 如果栈顶第一个int
型数值大于等于第二个,则跳转。if_icmpgt <branchoffset>
(If int compare greater than branch): 如果栈顶第一个int
型数值大于第二个,则跳转。if_icmple <branchoffset>
(If int compare less than or equal branch): 如果栈顶第一个int
型数值小于等于第二个,则跳转。ifnull <branchoffset>
(If reference is null branch): 如果栈顶reference
型数值为null
,则跳转。ifnonnull <branchoffset>
(If reference is not null branch): 如果栈顶reference
型数值不为null
,则跳转。
- 无条件跳转:
goto <branchoffset>
(Branch always): 无条件跳转到指定偏移量。goto_w <branchoffset>
(Branch always wide): 宽偏移量版本goto
,用于跳转范围更大的情况。
tableswitch
(Access jump table by index and jump): 用于switch
语句 (当case
值连续时)。lookupswitch
(Access jump table by key match and jump): 用于switch
语句 (当case
值不连续时)。
8. 异常处理指令:
athrow
(Throw exception or error): 将栈顶的异常对象抛出。checkcast <class>
(Check whether object is of given type): 检查对象类型,如果类型不匹配,抛出ClassCastException
(也可以用于类型检查,不仅仅是异常处理)。monitorenter
(Enter monitor for object): 尝试获取对象的 monitor (锁),用于synchronized
关键字的同步块的开始。monitorexit
(Exit monitor for object): 释放对象的 monitor (锁),用于synchronized
关键字的同步块的结束。
类型缩写:
在指令助记符中,通常会使用一些类型缩写来表示操作的数据类型:
i
: intl
: longf
: floatd
: doublea
: reference (对象引用)b
: bytec
: chars
: short
学习建议:
- 无需记住所有指令: 字节码指令数量庞大,不需要全部记住,重点掌握常用的指令和它们的功能。
- 结合实例学习: 通过查看 Java 代码编译后的字节码,结合具体的代码示例来学习指令,更容易理解。可以使用
javap -c <类名>
命令反编译.class
文件查看字节码。 - 关注指令类别: 按照功能类别 (操作数栈、局部变量表、运算、控制流等) 来学习,更容易形成体系。
- 查阅 JVM 规范: 如果需要深入了解某个指令的细节,可以查阅 JVM 规范 (The Java Virtual Machine Specification)。
- 理解 JVM 的基本执行模型: 基于栈的指令集、操作数栈、局部变量表等。
- 分析 Java 代码的性能: 通过分析字节码,可以了解代码的执行效率,找出潜在的性能瓶颈。
- 进行更深层次的 Java 调优: 理解字节码优化,可以编写更高效的 Java 代码。
- 深入理解 Java 语言特性: 例如,多态、异常处理、同步机制等在字节码层面的实现。