文章目录
对象头介绍
Java每个对象都包含两个部分, 对象头(Object Header)和对象体(Object Body). 对象头中存储代表该对象运行时的一些信息, 这部分信息与对象体的内容没有关系.
对象头被分为两个部分, 第一个部分是Mark Word, 代表标记信息, 例如hash code, 锁的标志位, GC分代年龄等; 第二个部分是Class Word, 代表类型信息, 该部分是一个指针, 指向方法区(元数据空间) 中的实际类型.
在32位虚拟机中, 对象头占64位, 其中Mark Word和Class Word各占32位. 而在64位虚拟机中, 不开启指针压缩的情况下对象头占128位, Mark Word和Class Word各占64位. 开启指针压缩之后, Class Word占32位, 对象头减少到占128位.
Mark Word对应到C++的代码
marOop.hpp
, Class Word对应到C++的代码kclass.hpp
关于文章推荐两篇, 其中一篇是英文, 相对简单, 另一篇是中文, 深入到源码
对象头详细结构 – 32位虚拟机
在32位的虚拟机中, 对象头的信息如下, 占用64比特(8字节), 数组额外占用4个字节表示长度.
由于在Mark Word
中要存储不同的多种信息, 按照对象状态的不同, 又可以细分为5种状态的Mark Word
.
- Normal – 普通状态, 由25位哈希码, 4位GC分代年龄, 1位偏向锁标识, 2位锁标志(01)组成.
- Biased – 偏向状态, 由23位线程标识, 2位时间戳, 4位GC分代年龄. 1位偏向锁标识, 2位锁标识(01)组成.
- Lightweight Locked – 轻量级锁状态, 由30位指针(指向锁记录), 2位锁标识(00)组成.
- Heavyweight Locked – 重量级锁状态, 由30位指针(指向重量级锁的
monitor
, monitor是一个数据结构, 会存储其他抢锁的线程), 2位锁标识(10)组成. - Mared for GC – 待GC回收状态, 只有最后2位锁标识(11)有效.
注意Normal State和Biased State的锁标志位都是01, 而是否偏向锁根据 biased_lock 判断.
Normal State
- hash code 是懒初始化, 它只会在调用
System#identityHashCode(Object)
方法之后被初始化. 在非普通状态下, hash code 将不在Mark Word中存储, 它会被存储到monitor
中 - GC分代年龄是一个数字, 如果它超过了 Tenuring-Threshold则会被移到老年代.
- biased_lock, 如果偏向锁启用, 则该位置是1, 否者是0
Normal State => biased_lock = 0, lock = 01
Biased State
- thread 是偏向的线程, 对象头中存储的是线程id
- epoch 是存储在 thread 位置线程的标识.
Biased State => biased_lock = 1, lock = 01
Lightweight State
- ptr_to_lock_record, 指向锁记录(存储在当前线程的栈帧), 在锁记录中存储了Mark Word的拷贝. 使用CAS更新该30位, 更新成功则表示获取了轻量级锁.
Heavyweight State
- ptr_to_heavyweight_monitor是一个指针, 指向
monitor
.
对象头详细结构 – 64位虚拟机
在64为虚拟机中, 对象头会变为128比特(16字节), 数组额外占用4个字节表示长度.
在开启指针压缩之后, 对象头 会变为12字节. 数组还是会额外占用4个字节表示长度.
写在最后的话
理解对象头是为了更好的理解Synchronized, 文中难免有纰漏的地方, 欢迎指正.