1. 概述
本文将深入探讨 boolean
类型在 JVM 中的内存占用情况。你可能以为 boolean
只占 1 bit?别急,真相没那么简单。
我们将借助 JOL(Java Object Layout)工具直观测量对象内存布局,并剖析背后的设计原理——包括对象头、字段对齐、压缩指针等影响因素。目标是搞清楚:一个 boolean
到底占多少字节?数组呢?为什么?
✅ 本文适合有一定 JVM 基础的开发者,避免“int
是 4 字节”这类基础科普。
2. 环境准备
要分析 JVM 内存布局,首选工具是 JOL(Java Object Layout),它能精确打印对象在堆中的结构。
引入依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
之后就可以通过 ClassLayout.parseClass()
或 ClassLayout.parseInstance()
查看类或实例的内存分布。
3. 基本类型内存占用
先看看 JVM 对基本类型的底层支持情况:
System.out.println(VM.current().details());
在默认开启压缩指针(Compressed Oops)的 64 位 JVM 中,输出如下:
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
关键信息提炼:
- ✅
boolean
/byte
:1 字节 - ✅
char
/short
:2 字节 - ✅
int
/float
:4 字节 - ✅
long
/double
:8 字节 - ✅ 引用类型(reference):4 字节(压缩后)
- ✅ 数组元素大小与字段一致
⚠️ 注意:每个 boolean
占 1 字节(8 bit),不是 1 bit!
3.1 关闭压缩指针的影响
即使通过 -XX:-UseCompressedOops
关闭压缩指针,boolean
大小依然不变:
# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
可以看到,引用类型从 4 字节变为 8 字节,但 boolean
仍是 1 字节。
❌ 所以别再幻想“用 boolean 节省内存”了——它根本不按 bit 存。
3.2 为什么不是 1 bit?防止 Word Tearing
你可能会问:为什么不用一个 bit 来存 boolean?
根本原因在于 原子性与地址访问限制。
现代 CPU 通常以字(word)为单位进行内存访问,无法原子操作单个 bit。如果多个 boolean 共享一个字节,修改其中一个 bit 可能会“误伤”邻近 bit,这种现象叫 Word Tearing。
JVM 明确禁止 Word Tearing(参考 JLS 17.6),要求每个字段/数组元素独立可寻址。
✅ 因此,JVM 选择用 1 字节存储 boolean,牺牲空间换安全和一致性。
4. 对象中的 boolean 字段内存布局
来看一个简单类:
class BooleanWrapper {
private boolean value;
}
使用 JOL 查看其内存布局:
System.out.println(ClassLayout.parseClass(BooleanWrapper.class).toPrintable());
输出:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 1 boolean BooleanWrapper.value N/A
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
结构拆解:
- ✅ 12 字节对象头:
- 两个
mark word
:存 GC 标记、锁状态、hashCode - 一个
klass word
:指向类元数据(runtime type info)
- 两个
- ✅ 1 字节:
boolean value
字段 - ✅ 3 字节填充(padding):用于对象对齐
对齐规则说明
JVM 默认要求对象大小为 8 字节对齐。当前总大小为 13 字节(12 + 1),需补 3 字节凑成 16。
⚠️ 这意味着:即使字段只占 1 字节,也可能因对齐浪费 3 字节。
4.1 自定义对齐的影响
通过 -XX:ObjectAlignmentInBytes=32
将对齐改为 32 字节:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 1 boolean BooleanWrapper.value N/A
13 19 (loss due to the next object alignment)
Instance size: 32 bytes
此时填充变为 19 字节,总大小 32 字节。
✅ 大对齐会显著增加内存开销,通常只在特定场景(如堆外内存)使用。
5. boolean 数组的内存布局
再看数组的情况:
boolean[] value = new boolean[3];
System.out.println(ClassLayout.parseInstance(value).toPrintable());
输出:
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) # mark word
4 4 (object header) # mark word
8 4 (object header) # klass word
12 4 (object header) # array length
16 3 boolean [Z.<elements> # [Z means boolean array
19 5 (loss due to the next object alignment)
结构分析:
- ✅ 16 字节对象头:
- 3 个 header 字(mark、klass)共 12 字节
- 额外 4 字节存数组长度(
array length
),这是数组特有的
- ✅ 3 字节数据区:3 个 boolean 元素,各占 1 字节
- ✅ 5 字节填充:使总大小对齐到 8 字节倍数(16 + 3 + 5 = 24)
⚠️ 小结:
- 每个
boolean[]
元素仍为 1 字节 - 但数组有额外 4 字节长度字段
- 加上对齐填充,小数组内存效率极低
例如:new boolean[1]
实际占用 24 字节(16 + 1 + 7 padding),有效数据仅 1 字节。
6. 总结
场景 | boolean 占用 | 说明 |
---|---|---|
单个字段 | 1 字节 + 对齐填充 | 最小 1 字节,常因对齐浪费 |
数组元素 | 每个 1 字节 | 数组有额外 4 字节长度 + 对齐 |
总体结论 | ❌ 不是 1 bit | JVM 为避免 Word Tearing,强制 1 字节对齐 |
✅ 关键结论:
boolean
在 JVM 中占 1 字节,不是 1 bit- 对象头(12~16 字节)和对齐填充是主要“内存黑洞”
- 小数组尤其浪费,不建议用
boolean[]
存大量标志位 - 如需极致节省空间,考虑
BitSet
或位运算(如long
用 64 bit 存 64 个 flag)
🔧 深入学习建议:
- 阅读 HotSpot 源码中的 oops 模块
- Aleksey Shipilëv 的经典文章:Java Objects Inside Out
示例代码已托管至 GitHub:https://github.com/baeldung/core-java-modules/tree/master/core-java-jvm-2