synchronized锁升级是mark word内存复用机制,仅动态切换其64/32位存储内容,不改变对象内存布局、大小及实例字段排布;锁状态通过末2位标识(01/00/10),hashcode迁移至monitor,gc年龄字段始终共存,monitor为堆中独立对象。
synchronized锁升级不是“额外加东西”,而是让对象头里的mark word动态复用同一块内存空间,根据锁状态切换存储内容。它不改变jvm整体内存布局(对象头+实例数据+对齐填充),但深刻影响对象头中mark word的位字段分配和语义解释。
Mark Word是锁升级的唯一载体
Java对象在堆中始终由三部分组成:对象头、实例数据、对齐填充。其中只有对象头里的Mark Word参与锁状态管理,其余两部分完全不受锁升级影响。锁升级全程只修改Mark Word的64位(64位JVM压缩指针下)或32位(32位JVM)内容,不新增字段、不扩大对象大小、不改动Klass Pointer或实例字段排布。
- 无锁状态时,Mark Word主要存hashCode、分代年龄、锁标识(01)和偏向位(0)
- 进入偏向锁后,hashCode被移出,腾出的高位存线程ID(54位)和epoch(2位),锁标识仍为01,但偏向位变为1
- 轻量级锁触发时,Mark Word清空原内容,写入指向当前线程栈中Lock Record的62位指针,锁标识变为00
- 升级为重量级锁后,该指针被替换为指向堆中ObjectMonitor对象的62位地址,锁标识变为10
不同JVM位宽直接影响锁字段容量
32位与64位JVM的Mark Word总长度不同,导致各锁状态下可存储信息量差异显著——这不是设计差异,而是位宽约束下的自然结果。
- 32位JVM:Mark Word仅32 bit,线程ID最多23位,Lock Record和Monitor指针各限30位,适合小规模并发场景
- 64位JVM(默认开启压缩指针):Mark Word仍为64 bit,但Class Pointer从64位压至32位,多出32位空间全用于锁相关字段——线程ID扩展到54位,Lock Record/Monitor指针达62位,支持超大堆与海量线程
- 关键一致点:锁标识始终占末2位(01/00/10/11),偏向位位置虽偏移(32位在bit 2,64位在bit 4),但作用完全相同——配合锁标识区分无锁与偏向锁
锁升级不改变对象内存占用,但影响GC与哈希行为
对象在堆中的大小始终由实例字段+对齐填充决定,锁升级本身不增加对象体积。但Mark Word内容变化会间接影响两类运行时行为:
-
hashCode迁移:无锁状态下调用
System.identityHashCode()结果存于Mark Word;一旦加锁(偏向/轻量/重量级),hashCode被“挤出”,转移到对应Monitor对象中;锁释放后也不会自动写回,后续再调用仍从Monitor读取 - GC分代年龄共存:age字段(4位)在所有锁状态下都保留,与锁标识、偏向位等复用同一区域,GC仍可正常读取并判断是否晋升老年代
- 对象头结构稳定:Klass Pointer长度随压缩开关变化(压缩后4字节,未压缩8字节),但Mark Word始终紧邻其前,整个对象头长度固定为12字节(压缩)或16字节(未压缩),对齐填充逻辑不变
Monitor对象是重量级锁的外挂存储
当锁升级到重量级,Mark Word不再存锁细节,而是指向堆中一个独立的ObjectMonitor对象。这个Monitor不在原对象内部,而是一个JVM在堆上分配的C++结构体,包含_owner(持有线程)、_count(重入计数)、_EntryList(阻塞队列)、_WaitSet(等待队列)等字段。
- Monitor本身有自己完整的对象布局(对象头+实例数据+填充),其生命周期独立于被锁对象
- 原对象Mark Word只存Monitor地址,相当于“指针跳转”,避免把复杂调度逻辑硬塞进对象头
- 这种分离设计使轻量级锁能快速CAS切换,重量级锁可承载完整线程调度能力,兼顾性能与功能
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











