文章目录
1 字节码概述
-
java字节吗对于虚拟机来说,属于基本执行指令
-
jvm的指令由一个字节长度(操作码)以及代表此操作所需参数(**操作数)**构成,由于jvm面向操作数栈,所以多数指令不包含操作数,只有操作码。
2 指令与数据类型关系
它们的操作码助记符中都有特殊的字符表明专门为那种数据类型服务:
-
i 代表对int类型的数据操作。
-
l 代表long
-
s 代表short
-
b 代表byte
-
c 代表char
-
f 代表float
-
d 代表double
注意:大部分指令都没有支持整数类型byte,char和short,编译器会在编译期或运行期将byte和short类型数据进行代符号扩展为相应的int数据类型,将boolean和char类型数据零位扩展为相应的int类型数据。
字节码指令按用途大致分为:
-
加载与存储指令
-
算术指令
-
类型转换指令
-
对象的创建与访问指令
-
方法调用与返回指令
-
比较控制指令
-
异常处理指令
-
同步控制指令
在进行做值相关操作时:
-
一个指令可以从局部变量表,常量池,堆中对象,方法调用,系统调用取到数据,将这些数据压入操作数栈。
-
一个指令也可以,从操作数栈取值进行,完成赋值,加减乘除,方法传参,系统调用等操作。
2.1 常用指令
-
局部变量压栈指令将局部变量加载到操作数栈:xload、xload_
x表示操作数类型 -
常量入栈指令 将常量加载到操作数栈:bipush,ldc
-
出栈转入局部变量 将一个数值从操作数栈存储到局部变量表:xstore
iload_这种指令他们表面上没有操作数,不需要进行取操作数,但其都隐含在指令中。
如:iload_0:将局部变量表中索引为0位置上的数据压入操作数栈中。
操作byte,char,short,和boolean类型数据时,经常用int类型指令来表示。
3 操作数栈
操作数栈,用来存放计算的操作数以及返回结果。
执行每一条指令之前,jvm要求该指令的操作数被压入操作数栈中,在执行指令时,jvm将该指令的操作数弹出,并将结果重新压入栈中。
4 局部变量表
字节码程序可以将计算结果缓存在局部变量区之中,实际上局部变量区就是一个数组,依次存放,this指针(非静态方法),所传入的参数,字节码中的局部变量。
和操作数栈一样,long类型以及double类型将占据两个单元。其余类型进占一个单元。
5 存储指令
5.1 局部变量压栈
局部变量压栈指令将给定的局部变量表中的数据压入操作数栈。x表示的是数据类型。
-
xload_(x为i,l,f,d,a, n为0到3)
-
xload(x为i,l,f,d,a)
5.2 常量入栈指令
将常数压入操作数栈中
-
const 系列:用于特定的常量入栈,入栈的常量隐含在指令本身中。
iconst_x(x为0到5) 将x压入栈
iconst_m1 将-1压入操作数栈
lconst_0, lconst_1 将长整数0,1压入栈
fconst_0,fconst_1,fconst_2 分别将浮点数0,1,2压入栈。
** 其中i表示整数,l表示长整数,f表示浮点数,d表示双精度浮点,a表示引用,如果含隐含操作,会以下划线给出**
- 指令push系列
由于count只能操作与 -1到5,所以其余使用push进行操作。
主要包括bipush(8为整数),sipush(16位整数)
- 指令ldc
万能指令
5.2 出栈装入局部变量表指令
出栈装入局部变量表用于将操作数栈中栈顶元素弹出,装入局部变量表的指定位置,用于给局部变量赋值。
- istore_n将从操作数栈中弹出一个整数,并将其给局部变量赋值。
6 算术指令
-
用于对两个操作数栈上的值进行运算,并将结果重新压入操作数栈中。
-
在每一类中jvm都有专门的算术指令,但没有直接支持byte,short,char,和boolean的算术指令,对于这些都是使用int类型进行处理。
算术指令:
6.1 i++与++i
//正常情况两者指令相同
void method(){
int i=10;
//i++;
++i;
void method(){
//此时是i为11,b为10.
int i=10;
int b=i++;
}
void method(){
//此时b是11,i是11.
int i=10;
int b= ++i;
}
7 类型转换指令
用于用户代码中的显示类型转换操作
7.1 宽化类型转换
-
jvm虚拟机直接支持以下数值的宽化类型转换,(小转大)也就是说不需要指令执行。
int–>long–>float–>double -
从int,long转float或long转double会发生精度丢失
-
byte,char,short类型到int类型的宽化类型转换不存在的,(一方面减少了指令,二 局部变量表的槽位固定是32位,无论byte,short都会占用32)
7.2 窄化类型转换
-
int转至byte,short,char,(i2b,i2c,i2s)
-
long类型转int (l2i)
-
float转int或long (f2i,f2l)
-
double 转int,long,float(d2i,d2l,d2f)
-
窄化类型准换会导致精度损失
8 对象创建与访问指令
8.1 创建指令
-
创建类实例指令:new
它接收一个操作数,指向常量池的索引,表示要创建的类型,执行完成后,将对象引用压入栈。 -
创建数组指令
- newarray:创建基本类型数组
- anewarray:创建引用类型数组
- multianerarray:创建多维数组
8.2 字段访问指令
对象创建后,就可以通过队象访问指令获取对象实例或数组实例中的字段或者数组元素了。
-
访问字段(static字段)的指令:getstatic,putstatic
-
访问类实例字段指令:getfield,putfield
8.3 数组操作指令
数组操作指令主要有:xastore和xaload
-
将一个数组元素叫再到操作数栈的指令:xaload
-
将操作数栈的值存储到数组元素的指令:xastore
说明:
-
xastore专门针对数组操作,以iastore为例,它用于给一个int数组的给定索引赋值,在iastore执行前操作数栈需要准备3个元素:值,索引,数组引用
-
指令xaload表示将数组的元素压栈,比如saload表示压入short数组。指令xaload在执行时,要求操作数中栈顶元素为数组索引i
8.4 类型检查指令
检查类实例或数组类型的指令:instanceof,checkcast
-
指令checkcast用于检查类型强制转化是否可以进行
-
指令instanceof用来判断给定对象是否是某一个类的实例
9方法指令
9.1方法调用指令
-
invokevirtual 根据对象的实际类型进行分派,支持多态。
-
invokeinterface 指令用于调用接口方法
-
invokespecial 包括实例初始化,私有方法和父类方法(都是静态绑定的)
-
invokestatic 用于调用类中的类方法 静态绑定
9.2 方法返回指令
方法调用结束前,进行返回,其指令是根据返回值的类型区分
- ireturn
- return 代表的是void 的方法
10 操作数栈管理指令
用于直接操作操作数栈的指令。
-
将一个元素或两个弹出并废弃:pop,pop2
-
复制栈顶数值一个或两个元素并重新压入栈顶:dup,dup2
-
将栈顶两个slot数值进行交换:swap
11 控制转移指令
11.1 比较指令
比较指令的作用是比较栈顶两个元素的大小,并将比较结果入栈。
比较指令有:dcmpg,dcmpl,fcmpg,fcmpl,lcmp
注意:boolean,引用数据类型不能比较大小。
11.2 条件跳转指令
条件跳转指令通常和比较指令结合使用,一般先用比较指令进行栈顶元素的准备,然后进行条件跳转。
12 异常处理指令
athrow 指令,是显示抛出异常的操作。
注意:在抛出异常时,java虚拟机会清除操作数栈上的所有内容,而后将异常实例压入调用者操作数栈上。