JVM实验一:Java基本类型
一.基本类型介绍
(1)Java中的基本类型
在Java堆中,各基本类型的空间占用为:
boolean(1B)、byte(1B)、char(2B)、short (2B)、int(4B)、float(4B)、double(8B)、long(8B)
在64位hotSpot的Java方法栈中,基本类型存储在局部变量表中,基本单位为slot,各基本类型的空间占用为:
boolean(1)、byte(1)、char(1)、short (1)、int(1)、float(1)、double(2)、long(2)
(2)boolean
boolean和boolean数组比较特殊,为了保证堆中boolean的合法性,会在存储时显示的进行掩码操作,只存储最后一位。例如boolean a = 3,存储到堆之前会对其进行掩码操作,a & 0x01 = 0x01。
当然在编译层面这样的复制就是不允许的。会进行掩码操作的基本类型包括boolean、byte、char 以及 short。
二.实验
(1)实验一:栈内赋值
1 public class Foo { 2 public static void main(String[] args) { 3 boolean boolValue = true; 4 // 将这个true替换为2或者3,再看看打印结果 5 if (boolValue) System.out.println("Hello, Java!"); 6 if (boolValue == true) System.out.println("Hello, JVM!"); 7 } 8 }
原Java代码中boolValue的赋值为true,现在修改它的字节码
1 # 编译Java文件,生成class文件 2 javac Foo.java 3 4 # 执行class文件 5 java Foo 6 # 输出: 7 # Hello, Java! 8 # Hello, JVM! 9 10 # 使用asmtool工具将class文件生成jasm文件 11 java -cp ./asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1 12 13 # 替换常量,将1(true)换为2 14 awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm 15 16 # 使用asmtool工具将jasm文件恢复成class文件 17 java -cp ./asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm 18 19 # 执行修改后的字节码文件 20 java Foo 21 # 输出:Hello Java!
上述的实验中,将boolValue修改为2后,执行了第一个判断,没有执行第二个判断(如果将booValue修改为3,结果相同)
先来看一下main方法的字节码:
1 iconst_1 2 istore_1 3 iload_1 4 ifeq 14 (+11) 5 getstatic #2 <java/lang/System.out> 6 ldc #3 <Hello, Java!> 7 invokevirtual #4 <java/io/PrintStream.println> 8 iload_1 9 iconst_1 10 if_icmpne 27 (+11) 11 getstatic #2 <java/lang/System.out> 12 ldc #5 <Hello, JVM!> 13 invokevirtual #4 <java/io/PrintStream.println> 14 return
首先,执行第一个判断的时候,调用的是ifeq(当栈顶元素是0的时候跳转)。此时栈内的boolean值为2,因此不跳转打印。
然后,执行第二个判断,调用语句为if_icmpne(比较栈顶两个元素,不相同则跳转)。此时栈内一个为2(修改的boolean值),一个为1(true),不相同因此不打印。
结论:栈内的boolean存储单元为1个slot,和byte、char、short 这些类型在栈上占用的空间是一样的。
(2)实验二:堆内赋值
1 public class Foo { 2 static boolean boolValue; 3 public static void main(String[] args) { 4 boolValue = true; 5 // 将这个true替换为2或者3,再看看打印结果 6 if (boolValue) System.out.println("Hello, Java!"); 7 if (boolValue == true) System.out.println("Hello, JVM!"); 8 } 9 }
javac Foo.java java -cp ./asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1 # 替换常量,将1(true)换为2 awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm java -cp ./asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm java Foo # 输出:无
现在,将booleanValue定义为静态变量,静态变量存储在方法区中(在有的虚拟机中,方法区是堆的一部分,这里可以把方法去理解为堆)。在之前的讲述中,存储到堆中时,会将boolean值进行掩码操作。当给booleanValue赋值2时,进行掩码之后的实际值为0,因此两处判断都不会执行打印。
javac Foo.java java -cp ./asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1 # 替换常量,将1(true)换为3 awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_3")} 1' Foo.jasm.1 > Foo.jasm java -cp ./asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm java Foo # 输出:
# Hello, Java!
# Hello, JVM!
当给booleanValue赋值3时,进行掩码之后的实际值为1,因此两处判断都会执行打印。
结论:堆内的boolean存储单元为1个字节,但是会在存储时显式进行掩码操作,只保留最后一位。

浙公网安备 33010602011771号