文章目录
- 1.八大基本数据类型
- 2.jvm规范描述
- 3.java字节码
- 4.代码证明
- 5.结论
近看到一个特别有意思的面试题,就是面试官问boolean占多少字节。一时间还不知道如何回答。我们先来看看java中的基本数据类型。
1.八大基本数据类型
java提供了8大基本数据类型:
数据类型 | 长度 | 位数 | 默认值 |
byte | 1Byte | 8 | 0 |
short | 2Byte | 16 | 0 |
int | 4Byte | 32 | 0 |
long | 8Byte | 64 | 0 |
float | 4Byte | 32 | 0.0f |
double | 8Byte | 64 | 0.0d |
boolean | - | - | false |
在一些资料上都有上述介绍。对于其他类型都好理解,唯独对于boolean,长度一直没有很好的说明。我们来看看jvm中是如何实现的。
2.jvm规范描述
首先来看看jvm规范对boolean的描述:
2.3.4 The boolean Type
Although the Java Virtual Machine defines a boolean type, it only provides
very limited support for it. There are no Java Virtual Machine instructions solely
dedicated to operations on boolean values. Instead, expressions in the Java
programming language that operate on boolean values are compiled to use values
of the Java Virtual Machine int data type.
The Java Virtual Machine does directly support boolean arrays. Its newarray
instruction (§newarray) enables creation of boolean arrays. Arrays of type
boolean are accessed and modified using the byte array instructions baload and
bastore (§baload, §bastore).
In Oracle’s Java Virtual Machine implementation, boolean arrays in the Java
programming language are encoded as Java Virtual Machine byte arrays, using 8 bits per
boolean element.
The Java Virtual Machine encodes boolean array components using 1 to represent
true and 0 to represent false. Where Java programming language boolean values
are mapped by compilers to values of Java Virtual Machine type int, the compilers
must use the same encoding.
大意是说,尽管在java虚拟机中定义了boolean类型,但是对它的支持是很有限的。没有专门针对boolean的java虚拟机指令,相反,java语言中操作boolean的表达式,在编译的时候,会被转换为int类型的值。
java虚拟机直接支持boolean数组,newarray指令允许创建boolean数组,使用字节数据的指令baload和bastore访问和修改boolean类型的数组。
在oracle的java虚拟机中,java语言中的boolean数组被编码为java虚拟机中的字节数组。每个boolean元素使用8位,1个字节来表示。
java虚拟机中使用1表示真,0表示假,来对boolean进行编码。如果编译器将java语言的boolean值映射位虚拟机类型的int类型,则编译器也必须使用相同的编码。
通过上述描述可以看出,实际上,boolean的变量是被当作int类型来处理的,而boolean数组,会被特殊处理。
3.java字节码
我们写一段java代码来进行验证:
package com.dhb.test;
import java.nio.ByteBuffer;
public class BooleanTest {
public static void main(String[] args) {
boolean a = false;
boolean[] array = {true,true,true,true,true};
}
}
通过javap来查看其字节码:
$ javap -c -l -s -verbose BooleanTest.class
Classfile /D:/workspace-git/MyProject/target/classes/com/dhb/test/BooleanTest.class
Last modified 2020-9-9; size 477 bytes
MD5 checksum ae4d3635b02ccda5fe262ef944987b72
Compiled from "BooleanTest.java"
public class com.dhb.test.BooleanTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // com/dhb/test/BooleanTest
#3 = Class #23 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lcom/dhb/test/BooleanTest;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 a
#16 = Utf8 Z
#17 = Utf8 array
#18 = Utf8 [Z
#19 = Utf8 SourceFile
#20 = Utf8 BooleanTest.java
#21 = NameAndType #4:#5 // "<init>":()V
#22 = Utf8 com/dhb/test/BooleanTest
#23 = Utf8 java/lang/Object
{
public com.dhb.test.BooleanTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/dhb/test/BooleanTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iconst_5
3: newarray boolean
5: dup
6: iconst_0
7: iconst_1
8: bastore
9: dup
10: iconst_1
11: iconst_1
12: bastore
13: dup
14: iconst_2
15: iconst_1
16: bastore
17: dup
18: iconst_3
19: iconst_1
20: bastore
21: dup
22: iconst_4
23: iconst_1
24: bastore
25: astore_2
26: return
LineNumberTable:
line 8: 0
line 9: 2
line 10: 26
LocalVariableTable:
Start Length Slot Name Signature
0 27 0 args [Ljava/lang/String;
2 25 1 a Z
26 1 2 array [Z
}
SourceFile: "BooleanTest.java"
可以看到,在定义变量 boolean a = false的时候:
0: iconst_0
1: istore_1
是将int类型的0压入栈顶。之后将int类型的值,存入第一个int类型的变量中去。我们可以对照本地变量表:
LocalVariableTable:
Start Length Slot Name Signature
0 27 0 args [Ljava/lang/String;
2 25 1 a Z
26 1 2 array [Z
slot为1的就是a。那么a=false实际上就是将a=1。
之后再来看对数组的操作:boolean[] array = {true,true,true,true,true};会转换为:
2: iconst_5
3: newarray boolean
5: dup
6: iconst_0
7: iconst_1
8: bastore
9: dup
10: iconst_1
11: iconst_1
12: bastore
13: dup
14: iconst_2
15: iconst_1
16: bastore
17: dup
18: iconst_3
19: iconst_1
20: bastore
21: dup
22: iconst_4
23: iconst_1
24: bastore
25: astore_2
首先是将int 5压入栈顶,之后用这个数字newarray 。之后将iconst_0 和iconst_1 执行bastore。意思就是将数组的第0位将int1设置。之后通过byte数组的方式 bastore到数组中。
实际上可以看出,在字节码中,boolean变量实际上与int等同。而boolean数组的操作,则与操作byte数组等价。
4.代码证明
我们可以用stackOverFlow上的一段代码进行证明,在hotSpot 1.8中,用如下代码证明:
package com.dhb.test;
public class BooleanTest1 {
private static final int SIZE = 1000000;
public static void main(String[] args) {
LotsOfBooleans[] first = new LotsOfBooleans[SIZE];
LotsOfInts[] second = new LotsOfInts[SIZE];
System.gc();
long startMem = getMemory();
for (int i = 0;i < SIZE; i++) {
first[i] = new LotsOfBooleans();
}
System.gc();
long endMem = getMemory();
System.out.println("Size for LotsOfBooleans: " + (endMem - startMem));
System.out.println("Average size: " + ((endMem - startMem) / ((double) SIZE)));
System.gc();
startMem = getMemory();
for (int i=0; i < SIZE; i++)
{
second[i] = new LotsOfInts();
}
System.gc();
endMem = getMemory();
System.out.println("Size for LotsOfInts: " + (endMem - startMem));
System.out.println("Average size: " + ((endMem - startMem) / ((double) SIZE)));
// Make sure nothing gets collected
long total = 0;
for (int i=0; i < SIZE; i++)
{
total += (first[i].a0 ? 1 : 0) + second[i].a0;
}
System.out.println(total);
}
private static long getMemory() {
Runtime runtime = Runtime.getRuntime();
return runtime.totalMemory() - runtime.freeMemory();
}
static class LotsOfBooleans {
boolean a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
boolean b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
boolean c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
boolean d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
boolean e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
static class LotsOfInts {
int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
}
其执行结果:
Size for LotsOfBooleans: 94654104
Average size: 94.654104
Size for LotsOfInts: 336376256
Average size: 336.376256
0
平均每个boolean数组的大小为94Bit,而boolean数组中有80个boolean,那么每个boolean的长度,不可能超过1个Byte。
5.结论
根据以上描述,可以得出结论:
boolean在oracle的jvm中,至少在HotSpot 1.8中,boolean变量的长度为4 Byte。而boolean数组中,每个boolean长度为1 Byte。
由于java不同jvm的实现实际上可能会有不同,以上结论仅限于hotSpot 版本。