核心概念

  1. 原始类型 boolean:

    • 理论最小:1 位 (0/1)
    • 实际占用:1 字节 (JVM 规范要求的最小可寻址单元)
    • 数组存储:boolean[] 中每个元素占用 1 字节
  2. 包装类型 Boolean:

    • 对象头:12 字节 (64位 JVM 开启压缩指针)
    • 字段值:boolean value 字段占用 1 字节
    • 对齐填充:3 字节 (内存对齐要求)
    • 总计:16 字节 (包含对象头和填充)
  3. 内存对比:

    boolean primitive;      // 1 字节
    Boolean wrapper = true; // 16 字节 (1600% 内存增长!)
    

操作步骤:测量内存占用

1. 添加 JOL 依赖 (Java Object Layout)
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>
2. 测量原始类型内存
// 测量boolean数组每个元素大小
System.out.println(VM.current().sizeOf(new boolean[100]) / 100.0);
// 输出: 1.0 (字节)
3. 测量包装类型内存
// 查看Boolean对象内存布局
System.out.println(ClassLayout.parseClass(Boolean.class).toPrintable());

// 测量单个Boolean实例
Boolean b = true;
System.out.println(GraphLayout.parseInstance(b).toFootprint());
// 输出: 
// java.lang.Boolean@76ed5528d footprint:
//     COUNT       AVG       SUM   DESCRIPTION
//        1        16        16   java.lang.Boolean
//        1                  16   (total)
4. 测量集合内存
// 测量List<Boolean>内存
List<Boolean> list = new ArrayList<>(Arrays.asList(true, false, true));
System.out.println(GraphLayout.parseInstance(list).totalSize());
// 输出: 144 字节 (3个Boolean对象+ArrayList结构)

常见错误

  1. 过度使用包装类型:

    // 错误:100个Boolean对象占用约 100*16 = 1600 字节
    List<Boolean> flags = new ArrayList<>(100); 
    
    // 正确:100个boolean仅占 100 字节
    boolean[] flags = new boolean[100];
    
  2. 自动装箱陷阱:

    // 每次循环创建新Boolean对象
    for (int i = 0; i < 1000; i++) {
        Boolean flag = (i % 2 == 0); // 自动装箱
    }
    
  3. 忽略对象头开销:

    // 误以为Boolean只占1字节
    Map<Boolean, String> map = new HashMap<>(); 
    // 实际每个Entry占用 32+ 字节
    

注意事项

  1. 内存对齐影响:

    • 对象大小总是 8 字节的倍数
    • 32位 JVM 对象头更小,但现代开发以64位为主
  2. JVM 参数影响:

    # 关闭压缩指针(对象头增至16字节)
    java -XX:-UseCompressedOops MyApp
    
  3. 布尔字段布局:

    class MyClass {
        byte a;      // 1 字节
        boolean b;   // 1 字节
        int c;       // 4 字节
    } // 总大小 = 12 字节 (头6 + 数据6 + 填充2)
    

使用技巧

  1. 字段排序优化:

    // 优化前:24字节
    class BadLayout {
        boolean b1;
        long l;    // 8字节
        boolean b2;
    }
    
    // 优化后:16字节
    class GoodLayout {
        long l;    // 8字节
        boolean b1;
        boolean b2;
    }
    
  2. 高效布尔数组:

    // BitSet比boolean[]省内存 (1位 vs 8位)
    BitSet flags = new BitSet(1_000_000);  // 占用 ~125KB
    boolean[] arr = new boolean[1_000_000]; // 占用 ~1MB
    

最佳实践与性能优化

  1. 内存敏感场景:

    • 优先使用 boolean 原始类型
    • 超大数据集使用 BitSet (压缩存储)
  2. 集合优化方案:

    // 替代 List<Boolean>
    class BooleanList {
        private long[] data; // 每个long存储64个布尔值
        public boolean get(int index) {
            int arrayIdx = index / 64;
            int bitIdx = index % 64;
            return (data[arrayIdx] & (1L << bitIdx)) != 0;
        }
    } // 1百万元素仅占 15.6KB
    
  3. 对象池优化:

    // 重用Boolean实例 (利用不可变性)
    Boolean getCached(boolean value) {
        return value ? Boolean.TRUE : Boolean.FALSE;
    }
    
  4. 序列化优化:

    // 使用原始类型序列化
    public void writeBoolean(DataOutput out, boolean value) {
        out.writeByte(value ? 1 : 0); // 比序列化对象高效
    }
    

总结对比表

场景 原始类型 包装类型 优化方案
单值内存 1 字节 16 字节 直接使用原始类型
10万元素数组 100 KB ≈1.6 MB 使用 BitSet (12.5 KB)
集合存储 N/A 高开销 自定义压缩结构
CPU缓存效率 字段重排序优化
GC压力 对象池/避免装箱