java中boolean类型基础数据在内存中占用的空间大小分析

boolean对象到底占用多少内存

0. 本文所用的java代码

package com.youai.test;
import org.openjdk.jol.info.ClassLayout;
/**
 * VM argument: E:\Java\jdk1.8.0_144\bin\java.exe 
 * -XX:+UnlockDiagnosticVMOptions #针对product版本的jvm需要这个解锁参数
 * -XX:+PrintAssembly #打印jit生成的汇编语言
 * -Xcomp  #让虚拟机不需预热直接使用jit编译, 而不是走解释器
 * -XX:CompileCommand=dontinline,*Bar.sum #指定jit编译不使用内联优化Bar.sum这方法
 * -XX:CompileCommand=compileonly,*Bar.sum  #指定只输出*Bar.sum方法的反编,避免产生太多无关的内容
 * com.youai.test.Bar > bar3.txt 
 *
 */
 
public class Bar {
    int a = 1;
    static int b = 2;
    boolean bo = false;
    boolean[] bos = new boolean[8];

    public int sum(int c, boolean bool) {
        bo = false;
        bos[0] = false;
        bos[1] = false;
        bos[2] = true;
        a = 123;
        return a + b + c;
    }

    public static void main(String[] args) {
        Bar bar = new Bar();
        bar.sum(3, false);
        System.out.println(ClassLayout.parseInstance(bar).toPrintable()); // 打印bar对象的内存布局
    }
}

1. 先看看jvm规范中对boolean对象的介绍

1. boolean

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.

用大白话的意思就是jvm规范对boolean类型的内存占用约束很宽松, 没用直接规定boolean占用多少内存, 具体占用多少由具体的虚拟机供应商实现(本文讨论的虚拟机是: hotspot). 知识java编译后的class对象中, boolean值由int类型值代替. (1代表true, 0代表false). 具体class反编译代码如下:

 //使用javap -v com.youai.test.Bar产生如下反汇编, 摘录关键部分
 public int sum(int, boolean);
    descriptor: (IZ)I
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: iconst_0
         2: putfield      #3                  // Field bo:Z ,此处就是设置sum方法中的bo=false, 上一条指令为iconst_0, 就是将int型的0放置到操作数栈, 这条指令使用int类型的0设置到bo成员变量上
         5: aload_0
         6: getfield      #4                  // Field bos:[Z
         9: iconst_0
        10: iconst_0
        11: bastore
        12: aload_0
        13: getfield      #4                  // Field bos:[Z

2. boolean数组

jvm规范对boolean数组的描述

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

boolean数组中的boolean与单独的boolean的并不一样, 在class文件中, boolean[]是通过byte[]来标记的, 还是看反编译的代码

// 在构造器中, new boolean[8]
14: aload_0
15: bipush        8
17: newarray       boolean // new了8个boolean, 从jvm规范中 newarray boolean等价于 newarray byte
19: putfield      #4                  // Field bos:[Z

// 在sum方法中, 对boolean数组的设置
5: aload_0
6: getfield      #4                  // Field bos:[Z
9: iconst_0
10: iconst_0                       
11: bastore                        // 此处数就是将0对象存放入byte数组中bs[0]
12: aload_0
13: getfield      #4                  // Field bos:[Z
16: iconst_1
17: iconst_0
18: bastore                     // 此处数就是将0对象存放入byte数组中bs[1]

2. 看看在hostspot虚拟运行时boolean占用的内存

使用jol工具打印内存布局, 其maven依赖为:

 <dependency>
     <groupId>org.openjdk.jol</groupId>
     <artifactId>jol-core</artifactId>
     <version>0.9</version>
 </dependency>

运行上述java代码, 控制台输出内容如下:

// bar对象的内存布局
com.youai.test.Bar object internals:
 OFFSET  SIZE        TYPE DESCRIPTION                               VALUE
      0     4             (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4             (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4             (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4         int Bar.a                                     123
     16     1     boolean Bar.bo                                    false  //可以看到此处bo占用内存是1byte
     17     3             (alignment/padding gap)                  
     20     4   boolean[] Bar.bos                                   [false, false, true, false, false, false, false, false]   // boolean数组在此处存储的是指针
Instance size: 24 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total

// bar.bos内存布局
[Z object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           05 00 00 f8 (00000101 00000000 00000000 11111000) (-134217723)
     12     4           (object header)                           08 00 00 00 (00001000 00000000 00000000 00000000) (8) //此处指示数组的长度
     16     8   boolean [Z.<elements>                             N/A //此处可以看出数组占用内存是8byte, 共8个对象, 所以boolean数组中每个对象占用内存也是1byte
Instance size: 24 bytes

3. 最后来看一下sum方法对应的jit汇编语言

1. 准备工作

  1. 使用hsdis工具生成反汇编语言, 将对应的hsdis共享库复制java_home/jre/bin/server目录中, 本文在windows中验证, 采用hsdis-amd64.dll共享库.
  2. 运行生成sum方法的汇编代码
cmd> jdk1.8.0_144\bin\java.exe -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*Bar.sum -
XX:CompileCommand=compileonly,*Bar.sum  com.youai.test.Bar 

2. 汇编代码分析验证

0x000000000317219b: movb   $0x0,0x10(%rdx)    ;*putfield bo ; 这里就是就是设置bar.bo = false, 这行指令就是将byte类型的0设置到bo指向的内存中
                                                ; - com.youai.test.Bar::sum@2 (line 17)

0x000000000317219f: mov    0x14(%rdx),%eax
0x00000000031721a2: shl    $0x3,%rax          ;*getfield bos
                                              ; - com.youai.test.Bar::sum@6 (line 18)

0x00000000031721a6: cmpl   $0x0,0xc(%rax)     ; implicit exception: dispatches to 0x0000000003172213
0x00000000031721ad: jbe    0x000000000317221d
0x00000000031721b3: movb   $0x0,0x10(%rax)    ;*bastore   ; 这行指令是bos[0] = (byte)0
                                              ; - com.youai.test.Bar::sum@11 (line 18)

0x00000000031721b7: cmpl   $0x1,0xc(%rax)
0x00000000031721be: jbe    0x000000000317222a
0x00000000031721c4: movb   $0x0,0x11(%rax)    ;*bastore    ; 这行指令是bos[1] = (byte)0
                                              ; - com.youai.test.Bar::sum@18 (line 19)

0x00000000031721c8: cmpl   $0x2,0xc(%rax)
0x00000000031721cf: jbe    0x0000000003172237
0x00000000031721d5: movb   $0x1,0x12(%rax)    ;*bastore    ; 这行指令是bos[2] = (byte)1
                                              ; - com.youai.test.Bar::sum@25 (line 20)

5. 补充: 在openj9虚拟机中boolean的内存占用

使用的测试代码

public class A {
    public int a = 10;
    public boolean b = false;
    public boolean c = true;
    public boolean d = false;
    public boolean e = true;
    public boolean f = false;
    public boolean g = true;
}

public class TestUnsafe {

    @Test
    public void test() {
        A a = new A();
        Class<?> c = A.class;
        Unsafe unsafe = getUnsafe();

        for(Field field: c.getDeclaredFields()) {
            System.out.println(field + " offset: " + unsafe.objectFieldOffset(field));
            if(field.getType() == boolean.class) {
                System.out.println(field + "=" + unsafe.getBoolean(a, unsafe.objectFieldOffset(field)));
            }
        }

    }


    public Unsafe getUnsafe() {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            return (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

控制台输出内容为

public int com.youai.test.A.a offset: 8
public boolean com.youai.test.A.b offset: 12
public boolean com.youai.test.A.c offset: 16
public boolean com.youai.test.A.d offset: 20
public boolean com.youai.test.A.e offset: 24
public boolean com.youai.test.A.f offset: 28
public boolean com.youai.test.A.g offset: 32

可见boolean在openj9的运行时, 占用4个byte

6. 总结

jvm规范中并没有强制定义boolean对占用内存大小的定义, 由虚拟机厂商自行实现. 通过runtime内存布局分析, 和jit汇编代码分析, hotspot虚拟机中boolean占用内存大小是1个byte, boolean数组中, 每个元素也只占用1个byte. 但在openj9这个虚拟机中, boolean占用内存大小是4个byte.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值