携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
java对象的组成
对象头+实例化数据+对齐填充
- 使用openJdk中的工具类JOL来分析java对象布局
// 引入maven
<!-- 使用JOL工具类,打印对象头 -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.8</version>
</dependency>
- 测试打印对象信息
public static void main(String[] args) {
L l = new L();
System.out.println(l);
String s = ClassLayout.parseInstance(l).toPrintable();
System.out.println(s);
}
- 打印结果信息
java的对象头
-
Java对象的对象头由 mark word 和 klass pointer 两部分组成
- mark word存储了同步状态、标识、hashcode、GC状态等等
- klass pointer存储对象的类型指针,该指针指向它的类元数据
- klass pointer中如果应用的对象过多,使用64位的指针将浪费大量内存。64位的JVM比32位的JVM多耗费50%的内存。我们现在使用的64位 JVM会默认使用选项 +UseCompressedOops 开启指针压缩,将指针压缩至32位
-
对象头信息
- 锁状态标识含义
| biased_lock | lock | 状态 |
|---|---|---|
| 0 | 01 | 无锁 |
| 1 | 01 | 偏向锁 |
| 0 | 00 | 轻量级锁 |
| 0 | 10 | 重量级锁 |
| 0 | 11 | GC标记 |
- lock: 锁状态标记位,该标记的值不同,整个mark word表示的含义不同
- biased_lock:偏向锁标记,为1时表示对象启用偏向锁,为0时表示对象没有偏向锁
- age:Java GC标记位对象年龄
- identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中
- thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念
- epoch:偏向时间戳
- ptr_to_lock_record:指向栈中锁记录的指针
- ptr_to_heavyweight_monitor:指向线程Monitor的指针
- 对象头前八位显示
| unused:1 | age:4 | biased_lock:1 | lock:2 |
|---|---|---|---|
| 0 | 0000 | 0 | 01 |
实例化数据
基本数据类型所占用大小
| 类型 | 大小(字节) |
|---|---|
| int | 4 |
| byte | 1 |
| long | 8 |
| short | 2 |
| float | 4 |
| double | 8 |
| char | 2 |
| boolean | 1 |
对齐填充
64位虚拟机上对象的大小必须是8的倍数
示例中对齐填充大小为7字节,填充完后对象大小为24字节