一个对象根据不一样状况能够被划分红两种状况,当对象是一个非数组对象的时候,对象头,实例数据,对齐填充在内存中三分天下,而数组对象中在对象头中多了一个用于描述数组对象长度的部分算法
对象头分为两部分,第一部分称之为**"Mark Word",第二部分是用于获取该对象类型的类型指针**,若是是数组对象还包括记录数组长度的数据。编程
在不一样的操做系统中,这些区域所占的内存也不一样,在32位的系统中,MarkWord占用32bit的空间(也就是4字节)。类型指针和数组长度数据同样合做占用32bit的空间。数组
在64位的操做系统中,MarkWord占用64bit的空间,类型指针在不开启指针压缩(CompressedOOPs)的状况下是64bit(8 byte),而在开启指针压缩的状况下,仅剩32bit(4 byte)数据结构
这一部分存储的是对象自身的运行时数据,这一起内容的数据结构并不固定,它会根据对象的状态复用本身的存储空间,并发
这是摘自markOop.hpp文件中的片断,其中表示了对象的如下五种状态:oop
标志位 | 偏向锁标识位 | 状态 |
---|---|---|
01 | 0 | 无锁 |
01 | 1 | 偏向锁 |
00 | 无 | 轻量级锁 |
10 | 无 | 重量级锁 |
11 | 无 | GC Mark |
咱们接下来接着去看MarkWord的结构:操作系统
在这里咱们能够看到,初始化的时候只是定义了无锁和偏向锁状态的结构(上半部分是没有开启COOPs-指针压缩的结构,下半部分是开启了指针压缩的结构),线程
当处于轻量级锁、重量级锁时,记录的对象指针,根据JVM的说明,此时认为指针仍然是64位,最低两位假定为0;当处于偏向锁时,记录的为得到偏向锁的线程指针,该指针也是64位;3d
更多的内容咱们就再也不这里扩展了,根据反馈的状况,我会在后面并发编程中单开一篇来聊聊锁的进化之路。指针
这个东西有时候会用到去肯定该对象属于哪一个类的实例,也有用不到的时候,这个要根据不一样的虚拟机对于对象的定位实现算法的选择来进行(好比HotSpot JVM就使用该类型指针去获取该对象类型数据)
实例数据是对象真正存储的有效信息,也是在程序代码中所定义的各类类型的字段内容,这里的字段内容不单单包括当前类的字段,也包括他的父类中所定义的字段。
这部分的存储规则遵循虚拟机分配策略参数和字段在Java源码中的定义顺序,HotSpot JVM默认的分配策略是long/double, int,short/char,byte/boolean,oops(普通对象指针,Ordinary Object Pointers)也能够理解为reference,关于指针压缩咱们下节去说。
这里须要注意,在父类中定义的变量会出如今子类前,可是咱们能够经过将CompactFileds参数设置为true,将子类中较小的变量插入到父类大变量的空隙中。
这部份内容并非必须存在的,由于Hot Spot JVM中规定了对象的大小必须是8字节的整数倍,在C/C++中相似的功能被称之为内存对齐,内存空间都是按照 byte 划分的,从理论上讲彷佛对任何类型的变量的访问能够从任何地址开始,但实际状况是在访问特定类型变量的时候常常在特定的内存地址访问,这就须要各类类型数据按照必定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
内存对齐遵循两个规则:
假设第一个成员的起始地址为0,每一个成员的起始地址(startpos)必须是其数据类型所占空间大小的整数倍
结构体的最终大小必须是其成员(基础数据类型成员)里最大成员所占大小的整数倍。
这里也就不难理解为何JVM规定对象的大小必须是8字节的整数倍了,由于在64位系统下(不开启指针压缩),对象中存在不少占用8 byte的数据类型。可是同时也存在一些4 byte的数据类型,这时咱们的Padding就起到了做用,去补充不满8 byte的部分,凑齐8的整数倍。