JVM基础 -- 基本类型
boolean类型
Java代码
1 | public class Foo { |
编译运行
1 | $ javac Foo.java |
修改字节码运行
1 | # jasm与javap的输出比较类似 |
1 | $ tail -n 23 Foo.jasm.bak | head -n 21 |
使用awk命令修改字节码
1 | $ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.bak > Foo.jasm |
1 | $ java -cp ./asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm |
Java语言规范+Java虚拟机规范
- Java语言规范:boolean类型只有两个取值:true和false,显然这两个符号是不能被虚拟机直接使用的
- Java虚拟机规范:boolean类型被映射成int类型,true被映射成1,false被映射成0
- 这个编码规则约束了Java字节码的具体实现
- 例如对于存储boolean数组的字节码,JVM需要保证实际存入的值为整数1或0
- 要求Java编译器也遵守这个编码规则,并且用整数相关的字节码来实现逻辑运算,以及boolean类型的条件跳转
- 因此,编译而成的class文件中,除了字段和入参外,基本看不出boolean类型的痕迹
- 这个编码规则约束了Java字节码的具体实现
基本类型
- 默认值看起来不一样,但在内存中都是0
- boolean和char是无符号类型,通常我们可以认定char类型是非负数,可以作为数组索引
浮点数
两个0
1 | private static String floatToHexIntBits(float f) { |
1 | float z1 = +0.0F; // +0.0F |
两个Infinity
- 正无穷:任意正浮点数(不含+0.0F)除以**+0.0F**得到的值
- 负无穷:任意正浮点数(不含+0.0F)除以**-0.0F**得到的值
- 正无穷和负无穷都是有确切的值的,分别是0x7F800000和0xFF800000
1 | public static final float POSITIVE_INFINITY = 1.0f / 0.0f; |
NaN(Not-a-Number)
- NaN:**[0x7F800001, 0x7FFFFFFF] U [0xFF800001, 0xFFFFFFFF]**
- 标准NaN:**+0.0f/+0.0f,0x7FC00000**
- 除了**!=** 始终返回true之外,其他所有的比较结果都会返回false
1 | float f = 1.0F; |
存储
- JVM每调用一个Java方法,都会创建一个栈帧
- 栈帧组成:局部变量表+操作数栈
- 局部变量表示广义的,包含实例方法的”this”指针和入参
- 局部变量表等价于一个数组,long和double 需要用2个数组单元来存储,其他基本类型和引用类型均占用1个数组单元
- boolean、byte、char、short、int、float和reference
- 32位HotSpot:在栈上占用4Bytes
- 64位HotSpot:在栈上占用8Bytes
- 这种情况仅存在于局部变量表中,并不会出现在存储在堆中的字段或者数组元素上
- boolean、byte、char、short、int、float和reference
- 将一个int类型的值,存储到堆中的char类型字段时,相当于做了一次隐式的掩码操作(0xFFFFFFFF -> ‘\uFFFF’)
- boolean数组直接用byte数组来实现
- 为了保证堆中的boolean值是合法的,HotSpot在存储时进行显式的掩码操作,只取最后一位的值存入boolean字段或数组
1 |
|
加载
- JVM的算数运算依赖于操作数栈,将堆中的boolean、byte、char以及short加载到操作数栈上,而后将栈上到值当做int类型来运算
- 对于boolean和char这两个无符号类型来说,加载伴随着零扩展
- 对于byte和short这两个有符号类型来说,加载伴随着符号扩展
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.