本章节主要来验证下是否开启压缩指针对对象头内存布局的影响,因为在其他地方看到的结论不一样,还是得眼见为实。先看下JVM版本以及默认启动参数:
|
|
由-XX:+UseCompressedClassPointers
可见,默认是开启压缩指针的。下面先引入一个依赖,用于查看对象内存布局:
|
|
开启压缩指针(默认)情况
|
|
由结果可以看出,对象一共占用16个字节,头部占12个字节。组成结构如下:
- Header:8字节Mark World + 4字节Class类型指针
- 实例数据:4字节的int类型成员变量
未开启压缩指针情况
|
|
由结果可以看出,对象一共占用24个字节,头部占16字节。组成结构如下:
- Header:8字节Mark World + 8字节Class类型指针
- 实例数据:4字节的int类型成员变量
- 对其填充:4字节,由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。所以对象内存布局中有了对其填充这一项。
由上面两个结果可以看出是否开启压缩指针,直接影响的是对象头部的大小,更具体的说是指向Class的类型指针那一部分。开启压缩时占4字节,未开启压缩时占8字节(64位的虚拟机)。
Mark World布局
顺便来看下Header的前8个字节Mark World区域,这8个字节存储了对象哈希码、对象分代年龄、锁状态等。根据最后一个字节的标志位,Mark World一共有5种表现形式,下面以64位的形式来看下这5种的布局情况:
正常对象,无锁,这是新建对象的Mark World
1| 25位未使用 | 31位HashCode(调用对象的hashCode后写入) | 1位未使用 | 4位age(所以age最大值为15) | 1位偏向锁标记(此处为0,表示不是偏向锁) | 2位锁状态(此处为01,结合前面1位表示未锁定) |偏向锁对象的Mark World
1| 54位线程ID | 2位epoch(偏向的时间戳) | 1位未使用 | 4位age(所以age最大值为15) | 1位偏向锁标记(此处为1,表示是偏向锁) | 2位锁状态(此处为01,结合前面1位表示可偏向) |轻量锁的Mark World
1| 62位指针,指向线程栈帧中的锁记录 | 2位锁标识(此处为00,表示轻量级锁定) |重量级锁的Mark World
1| 62位指针,指向关联的监视器对象 | 2位锁标识(此处为10,表示膨胀为重量级锁) |被GC标记过的Mark World
1| 62位空 | 2位状态标识(此处为11,表示被GC标记) |
本章节简单地验证了下对象头的内存布局,就到这儿了。