一.基本类型介绍
(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个字节,但是会在存储时显式进行掩码操作,只保留最后一位。