《深入理解java虚拟机》读书笔记(四)类文件结构-2
1.字节码指令简介
Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(操作码)以及跟随其后的零至多个代表此操作所需的参数(操作数)构成。Java虚拟机面向操作数栈,所以大多数指令都不包括操作数,只有一个操作码,指令参数都放在操作数栈中。
1.1 字节码与数据类型
在虚拟机的指令集中,大多数指令都包含其操作所对应的数据类型信息。但由于Java虚拟机的操作码长度只有一字节,指令集对于特定的操作只提供了有限的类型相关指令去支持它,并非每种数据类型和每一种操作都有对应的指令,有一些单独的指令可以在必要的时候用来将一些不支持的类型转换为可被支持的类型。
![](https://i-blog.csdnimg.cn/blog_migrate/2a21ac64a6fd3947325e718e2684d077.png)
从表1看来,大部分指令都没有支持整数类型byte、char和short,甚至没有任何指令支持boolean类型。编译器会在编译期或运行期将byte和short类型的数据带符号扩展为相应的int类型数据。对数组也是类似的处理方式。
1.2 加载和存储指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输。
- 将一个局部变量加载到操作栈:iload、iload_、lload、lload_、fload、fload_、dload、dload_、aload、aload_
- 将一个数值从操作数栈存储到局部变量表:istore、istore_
- 将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_ml、iconst_、lconst_、fconst_、dconst_
- 扩充局部变量表的访问索引的指令:wide
1.3 运算指令
算术指令用于对操作数栈上的两个值进行某种特定运算,并把结果重新存入到操作栈顶。大体上运算指令可以分为两种:对整型数据进行运算的指令与对浮点型数据进行运算的指令。对其他类型如byte、short、char和boolean类型的运算应使用操作int类型的指令代替。
- 加法指令:iadd、ladd、fadd、dadd
- 减法指令:isub、lsub、fsub、dsub
- 乘法指令:imul、lmul、fmul、duml
- 除法指令
- 求余指令
- 取反指令
- 位移指令
- 按位或指令
- 按位与指令
- 按位异或指令
- 局部变量自增指令
- 比较指令
1.4 类型转换指令
类型转换指令可以将两种不同的数值类型相互转换,这些转换操作一般用于实现用户代码中的显式类型转换操作,或者用来处理本节开篇所提到的字节码指令集中数据类型相关指令无法与数据类型一一对应的问题。
Java虚拟机直接支持以下数值类型的宽化类型转换:
- int类型到long、float或者double类型
- long类型到float、double类型
- float类型到double类型
处理窄化类型转换时,就必须显式地使用转换指令来完成,包括i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f。窄化类型转换可能会导致转换结果产生不同的正负号、不同的数量级的情况,可能会导致数值的精度丢失。