Java对象的布局

注意:我用的是jdk1.8版本的

在 HotSpot虚拟机中,对象在内存中的存储的布局可以分为三块区域:对象头(Header)实例数据(Instance Data)对齐填充(Padding):如下图

头歌中Java类和对象之对象数组 java对象头占几个字节_Word

对象头

  • Mark Word:包含一系列的标记位,比如轻量级锁的标记位,偏向锁标记位等等。在32位系统占4字节,在64位系统中占8字节;
  • Class Pointer(类型指针):用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在32位系统占4字节,在64位系统中占8字节;
  • Length:如果是数组对象,还有一个保存数组长度的空间,占4个字节;

实例数据

  • 对象实例数据 : 对象的所有成员变量其中包含父类的成员变量和本类的成员变量,也就是说,除去静态变量和常量值放在方法区,非静态变量的值是随着对象存储在堆中的。
  • byte、boolean是1个字节,short、char是2个字节,int、float是4个字节,long、double是8个字节,reference是4个字节(64位系统中是8个字节)。

对齐填充 

  • 由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,也就是说对象的大小必须是 8 字节的整数倍。对象头部分是 8 字节的倍数,所以当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

Java一个对象占多少字节,回归主题,根据上面所述,我们知道 占用16字节,下面具体详速:

  • 64位系统(未开启指针压缩):Mark Word占用8个字节 + Class Pointer占用8个字节 = 16个字节 (16已经是8的整数倍,所以不需要对齐填充)            对象头的大小:16个字节
  • 64位系统(开启指针压缩):Mark Word 占用8个字节 + Class Pointer 占用4个字节 + 对齐填充 4个字节 = 16个字节 (空对象,所以实例数据大小为0)   对象头的大小:12个字节

 上代码检测(注意:这里默认开启了指针压缩):

头歌中Java类和对象之对象数组 java对象头占几个字节_数据_02

综上我们可以看出实际的结果也是16字节,指针压缩Class Pointer的大小压缩成了4个字节

64位系统(开启指针压缩):Mark Word 占用8个字节 + Class Pointer 占用4个字节 + 对齐填充 4个字节 = 16个字节 (空对象,所以实例数据大小为0)   对象头的大小:12个字节

 

关闭指针压缩:设置参数: -XX:-UseCompressedOops 

头歌中Java类和对象之对象数组 java对象头占几个字节_Word_03

 64位系统(未开启指针压缩):Mark Word占用8个字节 + Class Pointer占用8个字节 = 16个字节 (16已经是8的整数倍,所以不需要对齐填充)

指针压缩

        开启指针压缩使用算法开销带来内存节约

         -XX:+UseCompressedOops 

注意:32位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。

注意:下面的所有测试,都是开启了指针压缩的

实例数据

example1. 添加一个int 类型

class TestO{
            int a;

        }

推测这个TestO的大小:

                                 对象头:Mark Word 8个字节 + Class Pointer 4个字节  = 12字节

                                 实例数据:int 占用 4个字节

                                TestO = 对象头 + 实例数据 = 12 + 4 = 16字节(因为16已经是8的整数倍,固这里不用对齐补充)

上结果:验证上面的推测完全正确 ,啦啦啦啦

头歌中Java类和对象之对象数组 java对象头占几个字节_数据_04

 

example2.对齐填充的位置

class TestO{
            byte b;
            String a;

        }

 推测这个TestO的大小:

                       24字节 => 12字节(对象头)+5个字节(实例数据:byte 1个字节+String 引用 4个字节)+7个字节(对齐填充)

                       但是 、但是、但是这里的对齐填充的位置真的是我们以为的那样 吗???

上结果:对齐填充的位置!!!!

头歌中Java类和对象之对象数组 java对象头占几个字节_头歌中Java类和对象之对象数组_05


在 HotSpot VM 中,对象排布时,间隙是在 4 字节基础上的(在 32 位和 64 位压缩模式下),上述例子中,byte为一个字节,空隙只剩下 3 字节,接下来的 String 对象引用需要 4 字节来存放,因此 byte 和对象引用之间就会有 3 字节对齐,对象引用排布后,最后会有 4 字节对齐,因此结果上依然是对齐需要7个字节

上图:

头歌中Java类和对象之对象数组 java对象头占几个字节_成员变量_06

 

example3.字段重排序

class TestO{
            byte b;
            String a;
            short c;
        }

推测这个TestO的大小:

             TestO = Mark Word 8个字节+Class Pointer 4个字节+byte 1个字节 +????到这里我们就有疑惑了?String 跟short??

上结果:字段重新排序了,我们发现 short排在了最前面,byte在后面,然后对齐填充(这里上面我们已经解释了原因),String在最后

short\char 会排在 byte/boolean的前面 

头歌中Java类和对象之对象数组 java对象头占几个字节_头歌中Java类和对象之对象数组_07

 

 

对象实例数据instance data : 对象的所有成员变量其中包含父类的成员变量和本类的成员变量,也就是说,除去静态变量和常量值放在方法区,非静态变量的值是随着对象存储在堆中的。验证结果在下方,请查收!

example4.静态变量

class TestO{
            byte b;
            static String name = "zx";
        }

上结果叻;并没有计算在内:所以验证了上面我们所说的静态变量不计算在对象的大小中

头歌中Java类和对象之对象数组 java对象头占几个字节_成员变量_08

 

example5:父类的成员变量

class TestO{
            byte b;
        }
        class TestC extends TestO{

        }

上结果:

头歌中Java类和对象之对象数组 java对象头占几个字节_Word_09

 

 

注意:

//使用:ClassLayout.parseInstance(o).toPrintable();

public static void main(String[] args) {
            TestC o = new TestC();
            String layout = ClassLayout.parseInstance(o).toPrintable();
            System.out.println(layout);
 }

需要在pom.xml里面引入:

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