一.基本类型介绍

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