mark word是对象头中8字节动态位域,不占独立字段,实时存储锁状态(01无锁/偏向锁、00轻量级锁、10重量级锁)、hashcode、gc年龄等;需用jol工具观测,不可用unsafe定位。

Java对象头里的锁状态标记,就藏在Mark Word那8字节里——它不占独立字段,却实时反映对象是否被锁、用的是哪种锁。理解它,不是为了背位图,而是看懂synchronized底层怎么“记住”锁的。
Mark Word不是字段,是动态位域
它没有Java层变量名,不能用Unsafe.objectFieldOffset()去查偏移;它是对象头中一块固定8字节(64位JVM,默认开启压缩指针)的内存区域,内容随对象状态实时切换:
- 无锁时,可能存对象的
hashCode或GC分代年龄(4位,最大15) - 偏向锁时,低2位为
01,后面紧跟着偏向线程ID和时间戳 - 轻量级锁时,高62位存指向当前线程栈中
Lock Record的指针 - 重量级锁时,存的是
ObjectMonitor*地址,也就是jstack里看到的locked <address></address>
锁标志位在哪?怎么读才靠谱
主流JDK 8u+默认开启偏向锁,锁状态由Mark Word最低2位决定,但必须结合高位交叉验证:
-
01:可能是无锁态,也可能是偏向锁——要看第3位(biased_lock位)是否为1,以及后续是否有有效线程ID -
00:轻量级锁 -
10:重量级锁 -
11:GC相关标记(如CMS mark-sweep阶段)
别硬写(mark & 0x3) == 0x1判断偏向锁——JVM运行时还依赖BiasedLocking::enabled()开关,关掉偏向锁后01就只代表无锁。
怎么亲眼看到Mark Word变化
用JOL(Java Object Layout)工具最直接:
- Maven引入:
<dependency><groupid>org.openjdk.jol</groupid><artifactid>jol-core</artifactid><version>0.9</version></dependency> - 代码中调用:
ClassLayout.parseInstance(obj).toPrintable() - 输出中的
mark:行就是当前Mark Word十六进制值,加锁前后对比,就能看到数值跳变
注意:对象地址不变,但mark值变了——因为Mark Word是对象头的一部分,而对象头与实例数据紧挨着,锁升级只是改写了这8字节的内容。
对象头整体布局(64位JVM + 压缩指针)
典型结构如下(单位:字节):
- Mark Word:8字节 —— 锁状态、hashCode、GC年龄等动态信息
- Klass Pointer:4字节 —— 指向类元数据(压缩指针下)
- Array Length(可选):4字节 —— 仅数组对象存在
所以普通对象头共12或16字节(有无数组长度),加上对齐填充后,对象起始地址始终是8字节对齐。这也是为什么JOL输出里offset 0–15常被标为“object header”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











