BYTECODES

Java对象的内存布局及访问

内存布局

在HotSpot虚拟机里,Java对象在堆中存储布局分为3部分:头信息、实例数据及填充数据。


头信息:包含2部分,一部分是对象运行时数据,比如hash code,锁状态,线程持有的锁,偏向线程ID, GC分代年龄等。这部分信息被称为"mark word"。考虑到虚拟机的空间效率,mark word被设计成动态定义的数据结构,根据状态复用自己的存储空间,以便在极小的空间里存储更多的数据。对象头的另一部分数据是类型指针,即对象指向它类型元数据的指针。并不是所有的虚拟机实现都在对象数据上保留类型指针。此外,如果是数组,对象头中还需要存储数组的长度,因为虚拟机需要知道Java对象的大小。


实例数据:存储代码定义的各种类型的字段数据,包含父类继承下来的。这部分数据的存储数据会受到虚拟机的分配策略和字段在源代码中的定义顺序影响。在默认的分配策略下,相同宽度的字段总是被分配到一起存放。在满足这个前提下,父类定义的变量会在子类前面。


填充数据:这个不是必须的,由于HotSpot虚拟机自动内存管理系统要求对象起始地址是8字节的整数倍。因此数据没有对齐的话,就会填充数据。

访问定位

对象的访问是通过栈上reference数据来操作堆上具体对象。但reference的实现有两种方式:一种是句柄,一种是直接指针。

如果使用句柄的话,Java堆中可能还会有句柄池,reference存放句柄的地址,句柄中包含了对象实例和类型数据的地址信息。

如果是使用指针,指针指向的是对象实例的地址,但对象的内存布局就需要考虑怎么存储类型数据的信息。


使用句柄的好处是对象移动后,不需要改变reference,只会改变句柄中实例数据指针。使用指针的好处是无需二次访问对象,效率更高。HotSpot使用直接指针这种方式。