环境
源码环境搭建:https://blog.csdn.net/qq_35040959/article/details/149060610?spm=1011.2124.3001.6209
源码版本:jdk24
oop(java每个对象的header结构)
源码位置: src/hotspot/share/oops/oop.hpp#L51-L60
源码:
cpp
class oopDesc {
private:
// 对象头 Mark Word - 使用 volatile 确保内存可见性
volatile markWord _mark; // 占用 1 Word(8 字节/64位系统)
// 类元数据联合体 - 通过指针压缩优化内存占用
union _metadata {
Klass* _klass; // 指向class类:普通指针(64位系统占8字节)
narrowKlass _compressed_klass; // 压缩指针(32位占4字节)
} _metadata; // 占用 1 Word(压缩时)或 2 Words(非压缩时)
}
对象头布局(内存布局)
对象头布局示例(64位系统):
---------------------------------------------------------
非压缩模式: [ markWord (8字节) | Klass* (8字节) ]
压缩模式: [ markWord (8字节) | narrowKlass (4字节) ]
markWord布局(记录对象基础信息)
- 初始状态(无锁):
bash
[31位哈希码 | 4位年龄 | 3位移位 | 0b01]
- 轻量级锁竞争:
bash
[64位栈锁记录地址 | 0b00]
- 锁膨胀过程:
bash
[ObjectMonitor* | 0b10]
源码位置:src/hotspot/share/oops/markWord.hpp:51
synchronized
字节码指令
monitorenter指令(上锁)
源码位置:InterpreterRuntime::monitorenter
加锁入口
源码位置:ObjectSynchronizer::enter
fast锁实现
源码位置:ObjectSynchronizer::enter_fast_impl
膨胀为重锁
源码位置:ObjectSynchronizer::inflate_impl
里面有一个无限循环,多线程竞争cas操作,直到有线程成功膨胀为重锁
重锁
源码位置:ObjectMonitor::enter
重锁无法退回到fast锁,所以升级后每次加锁还是会尝试cas直接修改锁拥有人

源码位置:ObjectMonitor::TrySpin
源码位置:ObjectMonitor::enter
进入安全点后即可处理gc等操作

源码位置:ObjectMonitor::EnterI
因为外层操作上循环:每次循环后都会重新尝试同过自旋获取锁

重锁-阻塞获取锁前的最后挣扎
源码位置:ObjectMonitor::EnterI
到这里可以发现 cas是低成本&高效的获取锁方案(避免的cpu切换上下文)

源码位置:src/hotspot/share/runtime/objectMonitor.cpp:834
真正进入阻塞获取后,不再自旋而是定时或等待唤醒后尝试cas获取锁

monitorexit 指令(下锁)
源码位置:ObjectMonitor::ExitEpilog
monitor流程图

八股文?爬!
八股文中介绍的轻量锁、偏向锁、重量锁真是害人不浅的无意义内卷;