java对象在堆中的基本内存结构,分为三个部分:


1.对象头(header):包括Mark Word(标记字段)和Class Pointer(类型指针)


2.实例数据(instance data):对象真正存储的有效信息,即代码中定义的各种类型的字段内容


3.对齐填充(padding):由HotSpot虚拟机定义对象起始地址必须是8字节整数倍,当不是整数倍时,需要填充数据补齐,因为对补齐的数据访问只需要一次内存访问即可



一、对象头


Mark Word:


1.用于存储对象自身运行时数据,如HashCode、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等


2.占用内存为一个机器码,32位系统一个机器码为4个字节,64位系统一个机器码为8字节


3.以32位系统为例:25Bit-对象的hashCode、4Bit-对象的分代年龄、1Bit-是否偏向锁、2Bit-锁标志位


4.通过MarkOop类型实现Mark Word,提供了大量方法用于查看当前对象头的状态,以及更新对象头的数据,为synchronized锁的实现提供了基础


5.不同锁状态下,存储数据不同,具体如下:





java 对象中的对象_内存 类中的成员变量也是类 java对象的内存结构_机器码



参考: http://www.importnew.com/23605.html



Class Pointer:


1.类型指针指向对象的类元数据,虚拟机通过这个指针确定该对象是哪个类的实例


2.占用内存为一个机器码,32位系统一个机器码为4个字节,64位系统一个机器码为8字节


3.所以一个对象头一般占用两个机器码,但是数组稍微特殊,使用三个机器码,其中一个机器码用来存放数组长度



二、实例数据


1.原生类型(primitive type)的内存占用如下:


Primitive Type

Memory Required(bytes)

boolean                      

1

byte                            

1

short                           

2

char                            

2

int                               

4

float                            

4

long                            

8

double    

8


2.引用类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes



三、对齐填充


1.因为对齐填充达到的目的是使对象的内存地址达到8的整数倍,所以有以下公式:


(对象头+实例数据+padding)%8==0



实际演练:


问题:64位系统下new int[0]、new int[1]、Integer a的对象大小?


new int[0]:对象头(24字节)+实例数据(0字节)+对齐填充(前面计算结果为24,是8的整数倍,所以对齐填充字节为0)=24字节


new int[1]:对象头(24字节)+实例数据(基本类型4字节)+对齐填充(前面计算结果为28,需要加4才是8的整数倍,所以对齐填充字节为4)=32字节


Integer a:对象头(16字节)+实例数据(引用类型8字节)+对齐填充(前面计算结果为24,是8的整数倍,所以对齐填充字节为0)=24字节



上面的计算结果均是在未开启指针压缩的情况下,如果开启指针压缩,则计算结果会有差别,具体关于指针压缩的理解见后面的章节: