在JVM中,对象在内存中分为三块区域:
- 对象头
- Mark Word(标记字段):默认存储对象的HashCode,分代年龄和锁标志位信息。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。
- Klass Point(类型指针):对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
- 实例数据
- 主要存放类的数据信息,父类的信息。
- 对其填充
- 虚拟机要求对象起始地址必须是8字节的整数倍
- 一个空对象占多少个字节?就是8个字节,是因为对齐填充的关系
Java对象头
每个Java对象都有对象头,synchronized用的锁是存在Java对象头里的。
如果是非数组类型,则用2个字宽来存储对象头,如果是数组,则会用3个字宽来存储对象头
长度 | 内容 | 说明 |
32/64bit | Mark Word | 存储对象的hashCode或锁信息等 |
32/64bit | Class Metadata Address | 存储到对象类型数据的指针 |
32/64bit | Array length | 数组的长度(如果是数组) |
Mark Word
Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。
锁状态 | 29 bit 或 61 bit | 1 bit 是否是偏向锁? | 2 bit 锁标志位 |
无锁 | 0 | 01 | |
偏向锁 | 线程ID | 1 | 01 |
轻量级锁 | 指向栈中锁记录的指针 | 此时这一位不用于标识偏向锁 | 00 |
重量级锁 | 指向互斥量(重量级锁)的指针 | 此时这一位不用于标识偏向锁 | 10 |
GC标记 | 此时这一位不用于标识偏向锁 | 11 |
- 当对象状态为偏向锁时,Mark Word存储的是偏向的线程ID
- 当状态为轻量级锁时,Mark Word存储的是指向线程栈中Lock Record的指针
- 当状态为重量级锁时,Mark Word为指向堆中的monitor对象的指针
参考资料:《深入浅出Java多线程》
《Java并发编程的艺术》