0
点赞
收藏
分享

微信扫一扫

Java对象在jvm堆内存中的存储布局

滚过红尘说红尘 2022-02-20 阅读 77

这次说的是64bit的jvm,暂不谈32bit的jvm,两者的对象大小是不一样的。

java对象的存储布局分为两种情况:普通对象 和 数据。

先说普通对象吧:如下图

 

那为啥对齐单元的大小是8个字节呢?

因为是64bit位的jvm,8个字节刚好是64个比特位,这样读取的效率高。

那这个东西我们在代码里面看得到吗? 可以看到的。

我们在pom.xml里面引入依赖:

 <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.14</version>
            <scope>provided</scope>                                                                                     <dependency>

但是我的报错啊:java.lang.ClassNotFoundException: org.openjdk.jol.info.ClassLayout,我也不知道为啥,解决办法如下:

点开如下链接:

  • Jar包下载地址

,选择一个版本,我选择的是0.10/这个版本,亲测可用:

然后选择格式符合: jol-cli-.-full.jar的包

在这里插入图片描述

然后将jar包放到项目里面,新建lib目录,放到lib目录下

 然后选中jar,将其添加为依赖,并且删除原来的maven依赖。

在这里插入图片描述

这样就可以用了。

 我们在代码里运行下:

1.0 情形:

public class Test {
   
}                                                                                   public class RunTest {
    public static void main( String[] args ) {
        Test test = new Test();
        System.out.println(ClassLayout.parseInstance(test).toPrintable());
    }
}

运行的结果如下:

奇怪了,怎么只有header和padding呢,实例数据那一部分呢?

实例数据是指成员变量,而Test.java里面没有成员变量。所以总共16个字节。

那如果是Object类型的对象呢,多少个字节?Object里面没有成员变量

1.1情形:

public class Test {
    int a;
}                                                                                   public class RunTest {
    public static void main( String[] args ) {
        Test test = new Test();
        System.out.println(ClassLayout.parseInstance(test).toPrintable());
    }
}

 

这种情况应该是多少个字节呢? int类型占4个字节。如下图:

header部分不变,依然是12个字节,成员变量a占4个字节,刚好16个字节。

那为什么这次没有填充数据呢?注意了,只有在对象大小不能被8整除的时候才需要填充数据。

那如果是这样呢:

public class Test {
    int a;
    int b;
}                                                                                       多少个字节? header部分不变12 bytes,两个int类型成员变量8 bytes,这个时候需要填充4 bytes, 加起来共24个字节。 

那如果是这样呢:

public class Test {
    int a;
    int b;
    boolean c;
}                                                                                     多少个字节? header部分不变12 bytes,两个int类型成员变量8 bytes,注意了boolean类型只占1个字节,所以需要填充3个字节,凑成24个字节。

那如果是这样呢:

public class Test {
    int a;
    int b;
    boolean c;
    String str = "hello";
}                                                                                     多少个字节?header部分不变12 bytes,两个int类型成员变量8 bytes,注意了boolean类型只占1个字节。字符串类型的是怎么算呢? 首先啊,字符串是一个特殊的对象,它是存放在字符串常量池中,那Test对象是多少字节呢?那str的大小为零吗 或者是5个字节吗?? str是句柄,引用类型的,是存储对象的地址,也就是普通对象的指针,这个指针占用多少个字节呢? 

 12 + 4 + 4 + 1 + 3 + 4 + 4 = 32 bytes。有两个问题哦:

1.  为什么编译器会将boolean类型转为int类呢?                                                                            2.  64位的计算机,指针应该是8个字节啊。64位的jvm,对象的指针为什么是4个字节呢?

首先回答问题1:

因为虚拟机规范只有 4字节 和 8字节, 大部分指令都没有支持 byte、char 和 short 类型,甚至没有任何指令支持 boolean 类型。编译器会在编译器或运行期将 byte 和 short 类型带符号扩展为 int 类型, boolean 和 char 类型零位扩展为相应的 int 类型。与之类似,在处理 boolean、byte、char 和 short 类型的数组时,也会转为使用相应的 int 类型的字节码来处理指令。 因此,大多数对于 boolean、byte、char 和 short 类型数据的操作,实际都是使用 int 类型作为运算类型。另外还有第二点原因,在设计虚拟机时,主要考虑的是 32位体系,32位系统使用 4 字节是最节省,因为 CPU 只能 32位32位的寻址。

都说 byte、boolen 类型占 1字节,但上面又提到, byte 会被提升为 int 类型,那么就应该占了 4字节类型(long、float), boolean、char、short 都是占了 4字节

再来回答问题2:

64bit的jvm,寻址能力也就是寻址范围是2的64次方啊,所以指针应该占用8个字节啊。但是jdk里面默认两个参数都开启了,一个是class对象的指针压缩,另一个是普通对象的指针压缩:

如下图,执行命令:java -XX:+PrintCommandLineFlags -version

 我们可以配置关闭这两个压缩:

将 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops                                            改为: -XX:-UseCompressedClassPointers -XX:-UseCompressedOops 

 关闭压缩后,再运行,就成了 32(关闭前) + 4 + 4 = 40bytes,如下图:

 本次就分享这么多,欢迎大佬们指正!

举报

相关推荐

0 条评论