在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充。
另外,Java中对象的内存布局分为两种情况,非数组对象和数组对象,区别是数组对象头中需要额外的空间存储数组的长度length。
对象头(Object Header)
对象头又分为MarkWord和Class Pointer。
-
MarkWord:标记存储对象本身运行时的数据,包含哈希值(指向对象在堆中的首地址值)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。在32位系统占4字节,在64位系统中占8字节。
-
ClassPointer:类型指针,存储的是对象指向它的Class对象(其对应的类元数据对象)的首地址。在32位系统占4字节,在64位系统中占8字节。
-
Length:只在数组对象中存在,用来记录数组的长度,占用4字节
对象头中Mark Word 在不同状态下的存储内容如下:
锁状态 | 存储内容(剩余bit) | 是否为偏向锁(1bit) | 锁标志位(2bit) |
无锁 | 对象哈希码、分代年龄 | 0 | 01 |
偏向锁 | 偏向线程ID、偏向时间戳、对象分代年龄 | 1 | 01 |
轻量级锁 | 指定锁记录的指针 | 无此bit | 00 |
重量级锁 | 指向重量级锁的指针 | 无此bit | 10 |
GC标记 | 空,不需要记录记录信息 | 无此bit | 11 |
实例数据(Instance data)
- Instance data:对象实例数据,存储本类对象的成员变量和所有可见的父类成员变量。(这里不包括静态成员变量,因为其是在方法区维护的)
对齐填充(Padding)
- Padding:Java对象的存储空间分配单位是8个字节,不足8字节,也会分配8字节进行填充。即所有Java对象占用bytes数必须是8的倍数,padding的作用就是补充字节,保证对象是8字节的整数倍。64位计算机上Java对象头的大小刚好是8的2倍,因此当实例数据部分没有对齐时,就需要通过对齐填充来补全。
指针压缩
-XX:+UseCompressedOops 可以压缩指针,将占用的空间压缩为原来的一半(4字节),起到节约空间的作用,classpointer参数大小就受到其影响。
Object o = new Object()到底占用多少个字节?
-
在开启指针压缩的情况下,markword占用8字节,classpoint占用4字节,Instance data无数据,总共是12字节,由于对象需要为8的整数倍,Padding会补充4个字节,总共占用16字节的存储空间。
-
在没有指针的情况下,markword占用8字节,classpoint占用8字节,Instance data无数据,总共是16字节。
END.