上一篇《并发编程(九)》我们已经知道:synchronized 锁的本质是对象锁,锁信息存在对象头里。但对象头到底长什么样?Mark Word 里存了什么?锁升级时,底层到底发生了什么变化?

这一篇,我们直接钻进 JVM 内存结构,讲一讲 对象头、Mark Word、锁状态、锁升级 。


一、Java 对象在内存里长啥样?

在 HotSpot 虚拟机里,一个对象在堆内存中分为三部分:

  1. 对象头(Object Header)
  2. 实例数据(Instance Data)
  3. 对齐填充(Padding)

我们关心的锁状态、哈希码、GC 年龄,全都存在 对象头 里。


二、对象头(Object Header)由什么组成?

HotSpot 的对象头分为两部分(数组多一个):

1. Mark Word(标记字)

  • 存储:哈希码、GC 分代年龄、锁状态标志、持有锁的线程 ID 等。
  • 这是 synchronized 锁升级的重要数据结构。

2. Klass Pointer(类型指针)

  • 指向方法区中这个对象的类元数据
  • JVM 通过它知道这个对象属于哪个类。

3. 数组长度(只有数组对象才有)

  • 占用 1 个字宽(Word)。
  • 类型是 int,所以 Java 数组最大长度是2^31 - 1

三、字宽(Word)与对象头大小

  • 32 位 JVM:1 Word = 4 字节
  • 64 位 JVM:1 Word = 8 字节

对象头大小:

  • 普通对象:2 个字宽
    • Mark Word:1 Word
    • Klass Pointer:1 Word
  • 数组对象:3 个字宽
    • 多 1 Word 存数组长度

四、Mark Word 核心字段(32 位 / 64 位结构一致)

Mark Word 是一个非固定结构,会随着锁状态变化而复用字段。

通用核心字段:

  1. hashCode:对象哈希码(未锁 / 偏向锁时存在)
  2. 分代年龄(Age)
    • 占 4 bit
    • 最大值 15(GC 最多 15 次晋升老年代)
  3. 锁状态标志位
    • 占 2~3 bit
    • 用来区分:无锁、偏向锁、轻量级锁、重量级锁、GC 标记
  4. 线程 ID / 指针
    • 偏向锁:存偏向线程 ID
    • 轻量级锁:指向栈中锁记录指针
    • 重量级锁:指向 Monitor 对象

五、锁状态与标志位

锁状态 标志位 说明
无锁 01 对象刚创建,未被竞争
偏向锁 01 无多线程竞争,锁偏向第一个线程
轻量级锁 00 轻度竞争,CAS 自旋
重量级锁 10 激烈竞争,OS 互斥量,线程阻塞
GC 标记 11 被 GC 标记,不是锁状态

关键点:

  • 无锁 & 偏向锁:标志位都是 01靠另外一位(偏向位)区分。

六、synchronized 锁升级完整流程

锁升级路线:无锁 → 偏向锁 → 轻量级锁 → 重量级锁只能升级,不能降级


1. 无锁状态

标志位:01

Mark Word 存储:hashCode、分代年龄、偏向位 0


2. 偏向锁(Biased Lock)

标志位:01,偏向位 1

适用场景:只有一个线程反复进入同步块

原理:

  • 第一个线程进入时,CAS 把线程 ID 写入 Mark Word
  • 下次该线程再来,直接进,不需要 CAS
  • 目的:减少无竞争场景下的 CAS 开销

偏向锁撤销:一旦有第二个线程竞争,偏向模式结束,升级为轻量级锁。


3. 轻量级锁(Lightweight Lock)

标志位:00

适用场景:线程交替执行同步块,轻度竞争

原理:

  • 线程在自己栈帧中创建 Lock Record
  • CAS 尝试将对象 Mark Word 指向这个 Lock Record
  • 成功:获取锁
  • 失败:自旋,不立即阻塞

升级条件:自旋次数 / 自旋线程达到阈值 → 膨胀为 重量级锁


4. 重量级锁(Heavyweight Lock)

标志位:10

Mark Word 指向 Monitor 对象

原理:

  • 依赖操作系统互斥量(Mutex)
  • 未获取锁的线程进入 Entry Set 阻塞
  • 锁释放时唤醒一个线程,再次竞争

特点:

  • 线程阻塞、唤醒 → 涉及用户态 / 内核态切换
  • 开销最大,只适合高竞争、长持有场景

七、锁升级的完整逻辑(一句话总结)

  1. 无锁:没人用
  2. 偏向锁:一个线程反复用
  3. 轻量级锁:线程交替用,轻度竞争
  4. 重量级锁:激烈竞争,大量线程阻塞

JVM 根据竞争激烈程度自动升级,目的是:在安全前提下,尽可能减少性能开销。


八、Mark Word 结构变化

不同锁状态下,Mark Word 存储内容完全不同:

  • 无锁:hashCode + 年龄 + 01
  • 偏向锁:线程 ID + epoch + 年龄 + 01
  • 轻量级锁:指向栈中锁记录的指针 + 00
  • 重量级锁:指向 Monitor 的指针 + 10
  • GC 标记:GC 相关信息 + 11

这就是为什么说:Mark Word 是 synchronized 锁升级的底层载体。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐