跳至主要內容

JVM对象内存布局

Yaien Blog原创大约 4 分钟JavaJVM

JVM对象内存布局

Hotspot虚拟机中,将对象在内存中存储的布局分为三块:对象头(Header)实例数据(Instance Data)对齐填充(Padding)

对象内存布局
对象内存布局

对象头

HotSpot主要将对象头划分为:MarkWordKlassPointer数组长度,记录了对象Hash码、对象所属年代、对象锁、锁状态、偏向锁的线程ID、偏向时间、数组长度等等信息

MarkWord

MarkWord用于存储对象自身运行时的数据,例如哈希码(HashCode)、GC分代年龄、锁状态、持有锁的线程、偏向线程ID、偏向时间戳等。这部分数据的长度在32位和64位的虚拟机中分别占32bit和64bit。

  • 32位MarkWord
32位MarkWord
32位MarkWord
  • 64位MarkWord
64位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

Header内存占用
Header内存占用

内存布局查看实战

为了验证对象内存布局,可使用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:对应内存中当前存储的值
上次编辑于:
贡献者: yanggl