JAVA中对象
对象内存布局(HotSpot)
概述
- 布局划分为三部分:对象头(Header)、实例数据(Instance)、对齐填充(Padding)
- 对象头包含两类信息:用于存储对象自身的运行时数据(Mark Word)和对象指向它的类型元数据的指针(类型指针)
- 实例数据部分时对象真正存储的有效信息,存储顺序受JVM分配策略参数和字段在java源码中定义顺序的影响,
- HotSpot默认存储顺序:long/double,int,short,char,byte/boolean,oop(Ordinary Object Pointer)
- 对齐填充部分不是必须存在,也没有任何含义,仅仅用于占位符
对象头(Header)
- 对象自身的运行时数据(Mark Word)
- 这部分数据的长度在32位和64位的未开启压缩指针的JVM中分别为32bit和64bit。
- Mark Word是一个有着动态定义的数据结构,目的在于在极小的空间存储更多的数据,根据对象的状态复用自己的存储空间
- 32位HotSpot中,如果对象未被同步锁锁定的状态下,Mark Word的32bit存储空间中,25bit用于存储对象哈希码,4bit存储对象分代年龄,2bit存储锁标志位,1bit固定为0。其他状态存储内容如下表
锁状态
25bit
4bit
1bit
2bit
23bit
2bit
是否偏向锁
锁标志位
无锁
对象的HashCode
分代年龄
0
01
偏向锁
线程ID
Epoch
分代年龄
1
01
轻量级锁
指向栈中锁记录的指针
00
重量级锁
指向重量级锁的指针
10
GC标记
空
11
- 类型指针
- JVM通过这个指针来确定该对象是哪个类型的实例
- 并不是所有JVM实现都必须在对象数据上保留类型指针。
- 如果对象是数组,对象头中还必须有一块用于记录数组长度的数据
New对象()
JVM创建对象过程
对象查找
概述
- reference类型在JVM规范中只规定了它是一个指向对象的引用,并没有定义这个引用应该通过什么方式去定位、访问到堆中对象的具体位置
- 主流访问方式有:使用句柄和直接指针
通过句柄访问
- JVM将在队中划分除一块内存作为句柄池,reference中存储对象的句柄地址,而句柄包含了对象实例和类型数据各自的地址信息
- 优势是:reference存储的是稳定句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而本身不用被修改
通过直接指针访问
- 堆中的内存布局必须考虑如何放置访问类型数据的相关信息,reference存储的就是对象地址
- 访问对象本身,不需要多一次间接访问开销,相对句柄,速度块,节省了一次指针定位的时间开销
- Hotspot主要使用此方法访问