字节码指令简析
java虚拟机是基于栈的架构,指令是由操作码和操作数组成。
- 操作码:1个字节的长度(0~255),因此指令集指令数不能超过256个
- 操作数:一条指令可以是0+个操作数,操作数可以是1+个字节。编译后的代码没有采用操作数长度对齐方式
不采用对齐方式的优劣点:
- 优势:可以省略掉很多的填充和间隔符号,减少了数据量
- 劣势:不对齐,那么在读取长字节的数据时,需要跨字节读取,需要花费更多的CPU时间。
未归类
- nop:0x00,啥都不做
- aconst_null:0x01,将null推送到栈顶
栈操作
load和store
- load 命令:用于将局部变量表的指定位置的相应类型变量加载到栈顶;
- store命令:用于将栈顶的相应类型数据保入局部变量表的指定位置;
iload 第1个int型变量进栈 istore 栈顶nt数值存入第1局部变量 iload_0 第1个int型变量进栈 istore_0 栈顶int数值存入第1局部变量 iload_1 第2个int型变量进栈 istore_1 栈顶int数值存入第2局部变量 lload 第1个long型变量进栈 lstore 栈顶long数值存入第1局部变量 aload 第1个ref型变量进栈 astore 栈顶ref对象存入第1局部变量
const 和 push 和 idc
- const:该系列命令主要负责把简单的数值类型送到栈顶。该系列命令不带参数。注意只把简单的数值类型送到栈顶时,才使用如下的命令。
- 局限性非常的小,只能把-1,0,1,2,3,4,5推送到栈顶
- push:负责把一个整形数字(长度比较小)送到到栈顶。该系列命令有一个参数,用于指定要送到栈顶的数字。
- idc:负责把数值常量或String常量值从常量池中推送至栈顶。该命令后面需要给一个表示常量在常量池中位置(编号)的参数
- final static定义的数值是在常量池中
- 对于const系列命令和push系列命令操作范围之外的数值类型常量,都放在常量池中
- 所有不是通过new创建的String都是放在常量池中的
pop和dup
- pop:用于栈顶数值的出栈操作,可以指定出栈的数值数量
- dup:用于复制栈顶的数值,并压入栈顶。可以指定复制和压入的次数
栈顶元素数学操作及移位操作系列
该系列命令用于对栈顶元素行数学操作,和对数值进行移位操作。移位操作的操作数和要移位的数都是从栈里取得。
- swap:将栈最顶端的两个数值互换(数值不能是long或double类型的)
- iadd:将栈顶两int型数值相加并将结果压入栈顶
- dmul:将栈顶两double型数值相乘并将结果压入栈顶
- frem:将栈顶两float型数值作取模运算并将结果压入栈顶
- lor:将栈顶两long型数值作“按位或”并将结果压入栈顶
- ... ...
对象相关
字段调用
方法调用
方法返回
对象与数组
- 创建类实例: new
- 创建数组:newarray、anewarray、multianewarray
- 数组元素 加载到 操作数栈:xaload (x可为b,c,s,i,l,f,d,a)
- 操作数栈的值 存储到数组元素: xastore (x可为b,c,s,i,l,f,d,a)
- 数组长度:arraylength
- 类实例类型:instanceof、checkcast
运算指令
类型转换
- 宽化转换(小->大),无需显示的转换指令,并且是安全的操作。
- 窄化转换,必须显示地调用类型转换指令。如i2b, i2c,f2i等等。
流程控制
控制指令是指有条件或无条件地修改PC寄存器的值,从而达到控制流程的目标
- 条件分支:ifeq、iflt、ifnull、ifnonnull等
- 复合分支:tableswitch、lookupswitch
- 无条件分支:goto、goto_w、jsr、jsr_w、ret
异常
Java程序显式抛出异常: athrow指令。
同步
方法级的同步和方法内部分代码的同步,都是依靠管程(Monitor)来实现的。
Java语言使用synchronized语句块,那么Java虚拟机的指令集中通过monitorenter和monitorexit两条指令来完成synchronized的功能。为了保证monitorenter和monitorexit指令一定能成对的调用(不管方法正常结束还是异常结束),编译器会自动生成一个异常处理器,该异常处理器的主要目的是用于执行monitorexit指令。
栈和局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表的容量以变量槽(slot)为最小单位,每个 slot 保证能放下 32 位内的数据类型。虚拟机通过索引定位的方式使用局部变量表,索引值从 0 开始。值得注意的是,对于实例方法,局部变量表中第 0 位索引的 slot 默认是 this引用;静态方法则不是。而且为了节约内存,slot 是可以重用的。