JVM对象内存布局
原创大约 4 分钟
JVM对象内存布局
Hotspot虚拟机中,将对象在内存中存储的布局分为三块:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)
对象头
HotSpot主要将对象头划分为:MarkWord、KlassPointer、数组长度,记录了对象Hash码、对象所属年代、对象锁、锁状态、偏向锁的线程ID、偏向时间、数组长度等等信息
MarkWord
MarkWord用于存储对象自身运行时的数据,例如哈希码(HashCode)、GC分代年龄、锁状态、持有锁的线程、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64位的虚拟机中分别占32bit和64bit。
- 32位MarkWord
- 64位MarkWord
MarkWord结构搞得那么复杂,是因为需要节省内存,让同一内存区域在不同锁阶段有不同的用处
- hash:保存对象的哈希码,在运行期间调用System.identityHashCode()进行计算,这是一个延迟计算,并将结果赋值到hash内存中
- age:保存对象的分代年龄。记录对象被GC的次数,当该次数到达阈值后就会由年轻代转入老年代
- biased_lock:偏向锁标识位。由于无锁和偏向锁的锁标识都是记01,为了区分引入了一位来标识是否为偏向锁
- lock:锁状态标识。区分锁状态,比如00时表示轻量锁,只有最后2位锁标识(00)有效
- JavaThread:保存持有偏向锁的线程ID。当处于偏行模式时,有线程持有对象,则对象的这里就会保存持有线程的ID,后续再获取锁时就无需再进行尝试获取锁的动作
- epoch:保存偏向时间戳。偏向锁再CAS锁操作过程中,偏向性标识,标识对象更偏向哪个锁
KlassPointer
KlassPointer又称类型指针,是指向对象的类元数据的指针。虚拟机可以通过这个指针来确定这个对象是那个类的实例。
在32位的JVM内,类型指针占4byte
在64位的JVM内,类型指针正常占8byte,若开启指针压缩或者最大堆内存小于32G时占4byte。JDK8后默认开启指针压缩
进行指针压缩的原因:
- 在64位平台的HotSpot中使用32位指针,内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据会占用较大宽带,同时GC也会承受较大压力
- JVM中32位地址最大支持4G内存(2的32次方),可以通过对对象指针的压缩编码、解码方式进行优化,使得JVM只用32位地址就可以支持更大的内存配置(小于等于32G)
需要注意的是:
- 堆内存小于4G时不需要开启指针压缩,JVM会直接去除32位地址,即使用低虚拟地址空间
- 堆内存大于32G时,压缩指针会失效,会强制使用64(8字节)来对Java对象进行寻址,所以堆内存最好不要大于32G。
数组长度
如果这个对象是一个数组,则对象头中会有一块4byte长度数数据区用于记录数组的长度,如果不是数组则这部分长度为0
内存布局查看实战
为了验证对象内存布局,可使用Java对象的内部布局工具JOL(JAVA OBJECT LAYOUT)
,用此工具可以查看new出来的一个java对象的内部布局,以及一个普通的java对象占用多少字节。
Maven依赖引入
<!-- JAVA对象布局、大小查看 -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
案例
public class Main {
public static void main(String[] args) {
System.err.println(ClassLayout.parseInstance(new Object()).toPrintable());
}
}
- 开启指针压缩
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0xf80001e5
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
- 未开启指针压缩
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 8 (object header: class) 0x000000001beb1c00
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
- OFF:偏移地址(Byte)
- SZ:内存占用大小(Byte)
- TYPE DESCRIPTION:类型描述,object header为对象头,object alignment gap为对齐补充
- VALUE:对应内存中当前存储的值