文章目录
- 一、对象在内存中的布局
- 1. 对象头
- 1.1. 哈希码(Hash Code):
- 1.2. 对象所属的年代(Generation):
- 1.3. 对象锁(Object Lock):
- 1.4. 锁状态标志(Lock State Flags):
- 1.5. 偏向锁(Biased Lock):
- 1.6. 偏向时间(Biased Time):
- 1.7. 数组长度(Array Length):
- 2. 实例数据
- 3. 对齐填充
- 二、对象头不同部分占用字节大小
- 1. Mark Word
- 2. Klass Pointer
- 3. Array Length
- 三、JOL工具验证
- 1. 添加依赖
- 2. 测试验证
- 四、正确解答
一、对象在内存中的布局
1. 对象头
1.1. 哈希码(Hash Code):
用于快速查找对象的哈希码。哈希码通常在对象创建时计算,并且可以用于在哈希表等数据结构中快速定位对象。
1.2. 对象所属的年代(Generation):
在分代垃圾回收算法中,对象会被分为不同的年代,用于优化垃圾回收的效率。对象头中可能包含一个标识对象所属年代的字段。
1.3. 对象锁(Object Lock):
用于实现对象级别的同步。对象锁可以被线程获取和释放,以保证对对象的操作的互斥性。
1.4. 锁状态标志(Lock State Flags):
用于记录对象锁的状态,如无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态等。锁状态标志可以用于优化锁的获取和释放的过程。
1.5. 偏向锁(Biased Lock):
用于提高无竞争情况下的锁性能。偏向锁是一种特殊的锁状态,当一个线程获取了对象的锁时,可以将锁标记为偏向锁,以后该线程再次获取锁时可以直接获取,无需进行额外的同步操作。
1.6. 偏向时间(Biased Time):
记录了对象上次偏向锁的时间。当其他线程尝试获取对象的锁时,会比较当前时间与偏向时间,如果时间差超过一定阈值,偏向锁会自动升级为轻量级锁。
1.7. 数组长度(Array Length):
对于数组对象,对象头中可能包含一个字段记录数组的长度。
2. 实例数据
- 实例数据是对象中存储的具体属性数据信息。
- 每个对象在创建时会根据其所属的类的定义来分配存储空间,用于存储类的属性数据。
- 实例数据包括了对象所属类的所有属性信息,包括父类的属性信息。在面向对象编程中,类定义了对象的属性和方法,而对象是类的实例化表示,因此每个对象都会有自己的一份实例数据来存储属性值。
- 总而言之,实例数据是对象中用于存储类的属性数据信息的部分,包括父类的属性信息。它是对象的核心组成部分,用于记录对象的状态和特征。
3. 对齐填充
-
在Java中,对象的大小需要按照8个字节或者8个字节的倍数进行对齐,这是为了避免伪共享问题。
-
伪共享是指多个线程同时访问不同的变量,但这些变量被缓存在同一个缓存行中,导致频繁的缓存行失效和刷新,降低了程序的性能。
-
为了避免伪共享问题,Java虚拟机会对对象进行对齐和填充。对齐是指按照一定的字节对齐方式,将对象的起始地址对齐到8个字节或者8个字节的倍数。填充是指在对象的末尾添加额外的字节,使得对象的大小满足对齐的要求。
-
通过对齐和填充,可以确保对象的起始地址和大小都满足对齐要求,从而避免伪共享问题。对于大多数的Java对象来说,它们的大小通常都是8个字节或者8个字节的倍数,因此可以有效地避免伪共享带来的性能损失。
-
需要注意的是,对齐和填充是由Java虚拟机自动进行的,开发者无需手动干预。在编写Java程序时,只需要关注对象的设计和使用,而无需过多关注对象的对齐和填充问题。
二、对象头不同部分占用字节大小
1. Mark Word
- 在32位的Java虚拟机中,Mark Word通常是32位,
- 在64位的Java虚拟机中,Mark Word通常是64位,
2. Klass Pointer
- 在32位的Java虚拟机中,Klass Pointer通常是4字节,用于指向类的元数据信息。
- 在64位的Java虚拟机中,Klass Pointer的大小可以根据虚拟机启用指针压缩(Compressed Oops)的方式和最大堆内存的大小来确定。
- 如果指针压缩被启用,或者最大堆内存小于32GB,那么Klass Pointer仍然是4字节。但是,如果指针压缩未启用并且最大堆内存大于等于32GB,那么Klass Pointer将会是8字节,以便能够支持更大的堆内存。
3. Array Length
- 当我们创建一个空的Object数组时,它不包含任何元素。
- 因此,该数组的长度为0,即没有任何元素被存储在数组中。
三、JOL工具验证
1. 添加依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
2. 测试验证
@Test
public void test() {
Object obj = new Object();
//查看对象内部信息
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
在未开启压缩指针的情况下:-XX:-UseCompressedOops
运行结果:
在(jdk1.6开始)默认开启压缩指针的情况下:
四、正确解答
- 一个空的对象,在开启压缩指针的情况下,占16个字节。
- 其中 Markword 占8个字节、类元指针占4个字节, 对齐填充占4个字节。