Java对象头Mark Word状态与锁膨胀过程剖析
- 前言
- 对象头Mark Word状态与锁膨胀过程剖析
-
- 1. 64位 HotSpot JVM 下 Mark Word 内存布局
-
- 64位 JVM Mark Word 位结构图表
- C++ 头文件位常量定义 (`markOop.hpp`)
- 64位 JVM Mark Word 位结构图表
- 2. 状态机迁移全景与锁演进路线
- 3. 偏向锁(Biased Locking)的加锁与撤销源码解析
-
- 3.1 偏向锁的快速进入(Fast Path)
- 3.2 偏向锁的撤销(Revocation)
- 3.1 偏向锁的快速进入(Fast Path)
- 4. 轻量级锁(Lightweight Locking)的入锁与压栈源码解析
-
- 4.1 BasicLock 结构
- 4.2 轻量级锁的加锁流程(`ObjectSynchronizer::fast_enter`)
- 4.1 BasicLock 结构
- 5. 核心膨胀逻辑:`ObjectSynchronizer::inflate` 源码逐行精析
- 6. 重量级锁(ObjectMonitor)的互斥与等待机制
-
- 6.1 `ObjectMonitor` 核心字段布局
- 6.2 竞争入口:`ObjectMonitor::enter`
- 6.3 线程入队与操作系统级挂起:`ObjectMonitor::EnterI`
- 6.4 释放锁与唤醒策略:`ObjectMonitor::exit`
- 6.1 `ObjectMonitor` 核心字段布局
- 7. 总结:
-
- 1、锁演进核心机制对比
- 2、Mark Word锁膨胀全景状态转换图
- 1、锁演进核心机制对比
前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。
对象头Mark Word状态与锁膨胀过程剖析
1. 64位 HotSpot JVM 下 Mark Word 内存布局
在 OpenJDK 8u 中,对象的内存布局分为对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头由 Mark Word(标记字)和 Klass Word(类型指针,可能被压缩为 32 位 narrowKlass)组成。
Mark Word 在 64 位 JVM (hotspot/src/share/vm/oops/markOop.hpp) 中占用 64 bits (8 Bytes)。JVM 巧用低位标志位(Tag bits)对不同锁状态下的内存结构进行复用,消除额外的内存开销。
64位 JVM Mark Word 位结构图表
| 锁状态 (Lock State) | 25 bits | 31 bits | 1 bit | 2 bits | 4 bits | 1 bit (biased_lock) | 2 bits (lock) | 说明 |
|---|---|---|---|---|---|---|---|---|
| 无锁 (Unlocked) | 未使用 | identity_hashcode | 未使用 | cms_epoch | age | 0 | 01 | 普通未加锁状态 |
| 偏向锁 (Biased) | __JavaThread (54bits)_* | epoch (2bits) | 未使用 | cms_epoch | age | 1 | 01 | 单线程独占持有 |
| 轻量级锁 (Lightweight) | ptr_to_lock_record (指向栈帧中 BasicLock 的指针,62bits) | 00 | 多线程无竞争 CAS 膨胀 | |||||
| 重量级锁 (Heavyweight) | ptr_to_heavyweight_monitor (指向 ObjectMonitor 的指针,62bits) | 10 | 存在激烈锁竞争 | |||||
| GC 标记 (Marked for GC) | ptr_to_forwardee (指向对象拷贝新地址,62bits) | 11 | GC 垃圾回收标记 |
C++ 头文件位常量定义 (markOop.hpp)
1// hotspot/src/share/vm/oops/markOop.hpp 2 3class markOopDesc: public ooDesc { 4 enum { 5 age_bits = 4, 6 lock_bits = 2, 7 biased_lock_bits = 1, 8 max_hash_bits = 31, 9 hash_bits = 31, 10 cms_bits = 1, 11 epoch_bits = 2, 12 13 // 位移偏移量计算 14 lock_shift = 0, 15 biased_lock_shift = lock_bits, // 2 16 age_shift = lock_bits + biased_lock_bits, // 3 17 cms_shift = age_shift + age_bits, // 7 18 epoch_shift = cms_shift + cms_bits, // 8 19 hash_shift = epoch_shift + epoch_bits, // 10 20 21 // 位掩码 (Masks) 22 lock_mask = right_n_bits(lock_bits), // 0x03 23 lock_mask_in_place = lock_mask << lock_shift, 24 biased_lock_mask = right_n_bits(biased_lock_bits), // 0x01 25 biased_lock_mask_in_place= biased_lock_mask << biased_lock_shift,// 0x04 26 biased_lock_bit_in_place = biased_lock_mask_in_place, 27 biased_lock_alignment = 1 << (biased_lock_shift + biased_lock_bits), 28 29 // 锁标记常量 (Tag Values) 30 locked_value = 0, // 00: 轻量级锁 31 unlocked_value = 1, // 01: 无锁 32 monitor_value = 2, // 10: 重量级锁 (ObjectMonitor) 33 marked_value = 3, // 11: GC 标记 34 biased_lock_pattern = 5 // 101: 偏向锁 (biased_lock:1, lock:01) 35 }; 36}; 37 38
2. 状态机迁移全景与锁演进路线
Java 对象在 HotSpot 中的锁生命周期是一个单向膨胀为主(偏向锁可被撤销为无锁或直接膨胀为轻量级锁)的状态机迁移过程:
1 ┌─────────────────────────┐ 2 │ 可偏向匿名对象 │ 3 │ (Biased, ThreadID = 0) │ 4 └────────────┬────────────┘ 5 │ 首次加锁 (CAS 写入 ThreadID) 6 ▼ 7┌──────────────┐ 撤销偏向 ┌─────────────────────────┐ 8│ 无锁状态 │ ◄───────────────┤ 偏向锁 │ 9│ (Unlocked) │ │ (Biased, ThreadID != 0) │ 10└──────┬───────┘ └────────────┬────────────┘ 11 │ │ 12 │ 多线程交替执行 │ 存在竞争 (安全点触发撤销) 13 ▼ ▼ 14┌──────────────────────────────────────────────────────────┐ 15│ 轻量级锁 │ 16│ (Mark Word 指向当前线程栈帧中的 BasicLock / Lock Record) │ 17└────────────────────────────┬─────────────────────────────┘ 18 │ 19 │ 存在激烈竞争 (CAS 自旋失败 / 膨胀函数被调用) 20 ▼ 21┌──────────────────────────────────────────────────────────┐ 22│ 重量级锁 │ 23│ (Mark Word 指向 C++ 堆中分配的 ObjectMonitor 结构) │ 24└──────────────────────────────────────────────────────────┘ 25 26
3. 偏向锁(Biased Locking)的加锁与撤销源码解析
偏向锁用于消除无竞争环境下的同步开销。通过在 Mark Word 中记录当前持有锁的 Java 线程 ID (JavaThread*),后续该线程进入同步块时仅需比较 Thread ID,避免任何 CAS 原子指令。
3.1 偏向锁的快速进入(Fast Path)
当 JVM 启用偏向锁 (-XX:+UseBiasedLocking,JDK 8 中默认延迟 4 秒启动) 时,解释器在执行 monitorenter 字节码时通过汇编/C++ 解释器直接校验 Mark Word:
1// hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp 2 3markOop mark = obj->mark(); 4if (mark->has_bias_pattern()) { // 检查 low 3 bits 是否为 101 5 // 校验当前 Mark Word 中的 epoch 是否有效,且线程 ID 是否等于当前线程 6 address current = (address) THREAD; 7 uintptr_t bits = (uintptr_t) mark; 8 if ((bits & (biased_lock_mask_in_place | topic_mask)) == 9 ((uintptr_t) current | prototype_header)) { 10 // 命中偏向锁,直接获得锁,无任何 CAS 开销 11 success = true; 12 } else if ((bits & biased_lock_mask_in_place) == 0) { 13 // epoch 过期,尝试重偏向 (Rebias) 14 // ... 15 } else { 16 // 偏向了其他线程,或者偏向锁未设置 Thread ID,使用 CAS 尝试偏向当前线程 17 markOop new_mark = (markOop) ((uintptr_t) mark | (uintptr_t) current); 18 if (Atomic::cmpxchg_ptr(new_mark, obj->mark_addr(), mark) == mark) { 19 success = true; 20 } else { 21 // CAS 失败,说明存在竞争,进入慢速路径 (Slow Path) 进行偏向锁撤销与锁膨胀 22 InterpreterRuntime::monitorenter(THREAD, elem); 23 } 24 } 25} 26 27
3.2 偏向锁的撤销(Revocation)
当另一个线程尝试获取已被偏向的对象锁时,偏向锁必须被撤销 (Revoke)。撤销过程必须等待全局安全点(Safepoint),以挂起偏向锁的持有者线程,检查其是否仍在同步块中。
核心实现源码位于 hotspot/src/share/vm/runtime/biasedLocking.cpp:
1BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) { 2 assert(get_safepoint_safepoint_counter() > 0, "must be at safepoint"); 3 4 markOop mark = obj->mark(); 5 if (!mark->has_bias_pattern()) { 6 return NOT_BIASED; 7 } 8 9 // 1. 获取偏向锁持有的 C++ 线程指针 JavaThread* 10 JavaThread* biased_thread = mark->biased_locker(); 11 12 // 2. 检查偏向线程是否存活 13 bool is_alive = false; 14 if (biased_thread != NULL) { 15 is_alive = Threads::is_supported_java_thread_obj(biased_thread->threadObj()) && 16 !biased_thread->is_terminated(); 17 } 18 19 // 3. 如果偏向线程已经终止,将对象重置为无锁状态或匿名偏向状态 20 if (!is_alive) { 21 if (attempt_rebias) { 22 obj->set_mark(markOopDesc::biased_locking_prototype()); 23 } else { 24 obj->set_mark(markOopDesc::prototype()); // 恢复为 01 无锁状态 25 } 26 return BIAS_REVOKED; 27 } 28 29 // 4. 如果偏向线程仍存活,遍历其调用栈帧 (Stack Walk),检查该对象是否还在被该线程锁住 30 GrowableArray<MonitorInfo*>* cached_monitors = get_or_compute_monitors(biased_thread); 31 BasicObjectLock* highest_lock = NULL; 32 for (int i = 0; i < cached_monitors->length(); i++) { 33 MonitorInfo* mon_info = cached_monitors->at(i); 34 if (mon_info->owner() == obj()) { 35 highest_lock = mon_info->lock(); // 找到了对应的 BasicObjectLock 记录 36 break; 37 } 38 } 39 40 if (highest_lock != NULL) { 41 // 偏向线程仍处于同步块内部:将偏向锁直接升级为轻量级锁 42 // 修改栈帧中最高层 BasicLock 的 Displaced Mark Word 为无锁原型 (prototype) 43 highest_lock->set_displaced_header(markOopDesc::prototype()); 44 45 // 直接将对象的 Mark Word 指向该线程栈帧中的 BasicLock 指针 46 obj->release_set_mark(markOopDesc::encode(highest_lock)); 47 } else { 48 // 偏向线程已不在同步块中:撤销偏向,恢复无锁状态 49 if (attempt_rebias) { 50 obj->set_mark(markOopDesc::biased_locking_prototype()); 51 } else { 52 obj->set_mark(markOopDesc::prototype()); 53 } 54 } 55 56 return BIAS_REVOKED; 57} 58 59
4. 轻量级锁(Lightweight Locking)的入锁与压栈源码解析
轻量级锁设计用于线程交替执行同步块(无激烈竞争)的场景。它的实现依赖于 Java 线程栈帧(Stack Frame)上的内存区域:BasicLock(锁记录 / Lock Record)。
4.1 BasicLock 结构
BasicLock 定义在 hotspot/src/share/vm/runtime/basicLock.hpp 中:
1class BasicLock VALUE_OBJ_CLASS_SPEC { 2 private: 3 volatile markOop _displaced_header; // 存储对象原有的 Mark Word 备份 4 public: 5 markOop displaced_header() const { return _displaced_header; } 6 void set_displaced_header(markOop header) { _displaced_header = header; } 7}; 8 9class BasicObjectLock VALUE_OBJ_CLASS_SPEC { 10 private: 11 BasicLock _lock; // Lock Record 实体 12 oop _obj; // 指向被加锁的 Java 对象 13}; 14 15
4.2 轻量级锁的加锁流程(ObjectSynchronizer::fast_enter)
核心代码位于 hotspot/src/share/vm/runtime/synchronizer.cpp:
1void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { 2 if (UseBiasedLocking) { 3 if (!SafepointSynchronize::is_at_safepoint()) { 4 // 偏向锁处理路径 5 BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); 6 if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) { 7 return; 8 } 9 } else { 10 BiasedLocking::revoke_at_safepoint(obj); 11 } 12 } 13 14 // 慢速/轻量级锁进入路径 15 slow_enter(obj, lock, THREAD); 16} 17 18void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { 19 markOop mark = obj->mark(); 20 21 // 1. 如果当前对象处于无锁状态 (mark->is_unlocked() 返回 true,即 low 2bits == 01) 22 if (mark->is_unlocked()) { 23 // a. 将对象当前的 Mark Word 备份到当前线程栈帧的 BasicLock 的 _displaced_header 中 24 lock->set_displaced_header(mark); 25 26 // b. 使用 CAS 尝试将对象的 Mark Word 修改为指向 BasicLock 的指针 27 // markOopDesc::encode(lock) 将指针转为 low 2bits == 00 的 markOop 28 if (Atomic::cmpxchg_ptr(markOopDesc::encode(lock), obj->mark_addr(), mark) == mark) { 29 TEVENT (slow_enter: fast stacklock); 30 return; // CAS 成功,轻量级锁获取成功 31 } 32 // CAS 失败,产生竞争,落入下面的锁膨胀逻辑 33 } 34 // 2. 如果对象已经是轻量级锁,检查是否是重入锁 (Lock Reentry) 35 else if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { 36 // 当前线程是该锁的持有者(重入):将 BasicLock 的 _displaced_header 设置为 NULL 作为重入标记 37 lock->set_displaced_header(NULL); 38 return; 39 } 40 41 // 3. 产生多线程并发竞争,或者二次重入校验失败:触发锁膨胀 (Inflation) 升级为重量级锁 42 lock->set_displaced_header(markOopDesc::unused_mark()); 43 ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD); 44} 45 46
5. 核心膨胀逻辑:ObjectSynchronizer::inflate 源码逐行精析
当轻量级锁 CAS 失败,或者调用了 Object.wait() / Object.hashCode() 方法(重写了 Mark Word 导致轻量级锁无处存放 HashCode)时,JVM 必须将锁膨胀 (Inflate) 为重量级锁 ObjectMonitor。
ObjectSynchronizer::inflate() 是 OpenJDK 中最关键的锁转换状态机实现(采用自旋锁与 CAS 控制状态转移),定义在 hotspot/src/share/vm/runtime/synchronizer.cpp。
1ObjectMonitor* ObjectSynchronizer::inflate (Thread * Self, oop object) { 2 // 避免自旋时的无谓计算 3 OrderAccess::fence(); 4 5 for (;;) { 6 const markOop mark = object->mark(); 7 assert (!mark->has_bias_pattern(), "invariant"); 8 9 // =================================================================== 10 // 情况 1:锁已经是重量级锁 (Inflated),最简单直接的路径 11 // =================================================================== 12 if (mark->has_monitor()) { 13 ObjectMonitor * monitor = mark->monitor(); 14 assert (monitor->object() == object, "invariant"); 15 return monitor; // 直接返回已存在的 ObjectMonitor 指针 16 } 17 18 // =================================================================== 19 // 情况 2:膨胀中状态 (INFLATING),其他线程正在对此对象进行 inflate 操作 20 // =================================================================== 21 if (mark == markOopDesc::INFLATING()) { 22 // 遇到过渡态,通过 spin_yield 避让,等待其他线程完成膨胀 23 tevent(Inflate: Spin); 24 ReadStableMark(object); // 自旋等待 Mark Word 变为 stable 25 continue; 26 } 27 28 // =================================================================== 29 // 情况 3:当前为轻量级锁 (Lightweight Locked) 状态 30 // =================================================================== 31 if (mark->has_locker()) { 32 // 1. 从全局预分配池或线程私有块 (omInUseList) 中分配一个未使用的 ObjectMonitor 33 ObjectMonitor * m = omAlloc (Self); 34 35 m->Recycle(); 36 m->_mark = mark; 37 38 // 2. 将 Mark Word 设置为 INFLATING() (位标记为 00,但 displaced mark 为 NULL) 39 // 使用 CAS 尝试争夺膨胀控制权 40 markOop cmp = object->cas_set_mark(markOopDesc::INFLATING(), mark); 41 if (cmp != mark) { 42 // CAS 争夺失败,释放刚才分配的 monitor,重新自旋 43 omRelease(Self, m, true); 44 continue; 45 } 46 47 // 3. CAS 成功!当前线程独占膨胀控制权 48 // 提取轻量级锁在持锁线程栈帧上的 Displaced Mark Word 49 markOop dmw = mark->locker()->displaced_header(); 50 assert (dmw != NULL, "invariant"); 51 52 // 如果轻量级锁是重入状态 (dmw == NULL),需要向上遍历栈帧寻找最初的那个 非NULL Displaced Mark Word 53 if (dmw == NULL) { 54 // 逻辑简化:恢复对象原始的无锁原型 55 dmw = markOopDesc::prototype(); 56 } 57 58 // 4. 初始化 ObjectMonitor 内部字段 59 m->set_header(dmw); // 转移保存原始的 Mark Word (包含 identity hash code 等) 60 m->set_owner(mark->locker()); // 暂时将 owner 设置为持有轻量级锁的 BasicLock 指针 61 m->set_object(object); 62 63 // 5. 关键原子的状态更新:发布重量级锁 64 // 将对象的 Mark Word 原子更新为指向 ObjectMonitor 实体,并附加 tag = 10 (monitor_value) 65 object->release_set_mark(markOopDesc::encode(m)); 66 67 // 6. 返回创建成功的 ObjectMonitor 68 return m; 69 } 70 71 // =================================================================== 72 // 情况 4:当前处于无锁状态 (Unlocked, low 2bits == 01) 73 // =================================================================== 74 assert (mark->is_unlocked(), "invariant"); 75 76 // 1. 分配 ObjectMonitor 节点 77 ObjectMonitor * m = omAlloc (Self); 78 79 m->Recycle(); 80 m->set_header(mark); // 直接保存当前无锁的 Mark Word 81 m->set_owner(NULL); // 暂无可竞争的 Owner 82 m->set_object(object); 83 84 // 2. CAS 将对象 Mark Word 直接指向 ObjectMonitor 指针 (tag 10) 85 if (object->cas_set_mark(markOopDesc::encode(m), mark) != mark) { 86 // CAS 失败,说明有其他线程竞争加锁或膨胀,释放 monitor 并重试 87 m->set_object(NULL); 88 omRelease(Self, m, true); 89 continue; 90 } 91 92 // 3. 膨胀成功,返回 ObjectMonitor 93 return m; 94 } 95} 96 97
6. 重量级锁(ObjectMonitor)的互斥与等待机制
一旦锁演变膨胀为重量级锁,后续所有的竞争与挂起逻辑由 C++ 实现的 ObjectMonitor 对象接管。
6.1 ObjectMonitor 核心字段布局
结构定义在 hotspot/src/share/vm/runtime/objectMonitor.hpp:
1class ObjectMonitor { 2 markOop _header; // 存储原始的 Mark Word (包含 HashCode、age 等) 3 void* _object; // 指向该 Monitor 关联的 Java 对象引用 4 void* _owner; // 指向当前持有锁的线程 (JavaThread*) 或 BasicLock 指针 5 6 intptr_t _recursions; // 锁重入次数 (0 表示无重入) 7 intptr_t _count; // 挂起等待锁的线程计数 8 9 ObjectWaiter* _cxq; // 竞争队列 (ContentionQueue):Lock-free LIFO 栈,单向链表 10 ObjectWaiter* _EntryList; // 双向链表:准备获取锁的候选线程队列 11 ObjectWaiter* _WaitSet; // 双向链表:调用 Object.wait() 被挂起的线程队列 12 13 Thread* _QueueWaiter; // 队列保护锁标识 14 int _WaitSetLock; // 保护 WaitSet 的互斥信号量 15}; 16 17
6.2 竞争入口:ObjectMonitor::enter
当线程试图获取重量级锁时,调用 ObjectMonitor::enter (hotspot/src/share/vm/runtime/objectMonitor.cpp):
1void ATTR ObjectMonitor::enter(TRAPS) { 2 Thread * const Self = THREAD; 3 void * cur; 4 5 // 1. 快速 CAS 尝试获取锁:将 _owner 从 NULL 修改为当前线程 6 cur = Atomic::cmpxchg_ptr(Self, &_owner, NULL); 7 if (cur == NULL) { 8 return; // 抢锁成功,直接返回 9 } 10 11 // 2. 检查是否重入 (Self 即为当前持有锁的线程) 12 if (cur == Self) { 13 _recursions++; 14 return; 15 } 16 17 // 3. 检查当前 owner 是否为轻量级锁指针 (如果刚才由轻量级锁膨胀而来,owner 暂存为 BasicLock*) 18 if (Self->is_lock_owned((address)cur)) { 19 assert(_recursions == 0, "internal error"); 20 _recursions = 1; 21 _owner = Self; // 修正 owner 为当前 C++ JavaThread* 指针 22 return; 23 } 24 25 // 4. 竞争失败,准备进入阻塞队列 (Slow Path) 26 Self->_ParkEvent->Reset(); 27 28 // 5. 自旋优化尝试 (Adaptive Spinning) 29 if (Knob_SpinLimit > 0) { 30 if (TrySpin(Self) > 0) { 31 return; // 自旋成功获取到了锁 32 } 33 } 34 35 // 6. 自旋失败,加入 cxq / EntryList 并挂起线程 36 EnterI (THREAD); 37} 38 39
6.3 线程入队与操作系统级挂起:ObjectMonitor::EnterI
1void ATTR ObjectMonitor::EnterI (TRAPS) { 2 Thread * Self = THREAD; 3 4 // 1. 创建 ObjectWaiter 包装节点 5 ObjectWaiter node(Self); 6 Self->_ParkEvent->Reset(); 7 8 // 2. 利用 CAS 将当前节点压入 _cxq 栈顶 (无锁 LIFO 链表) 9 ObjectWaiter * nxt; 10 for (;;) { 11 node._next = nxt = _cxq; 12 if (Atomic::cmpxchg_ptr(&node, &_cxq, nxt) == nxt) break; 13 14 // 再次尝试抢锁 15 if (TryLock (Self) > 0) return; 16 } 17 18 // 3. 阻塞循环:调用 OS 底层 API 挂起当前线程 19 for (;;) { 20 if (TryLock (Self) > 0) break; // 再次尝试获取锁 21 22 // 基于 OS 的底层的 park 机制 (Linux 下为 futex / pthread_cond_wait) 23 Self->_ParkEvent->park(); // 线程在此处被挂起 (Suspend) 24 25 // 线程被 unpark 唤醒后继续循环抢锁 26 } 27 28 // 4. 抢锁成功,从队列中移除自身节点 29 UnlinkAfterAcquire(Self, &node); 30} 31 32
6.4 释放锁与唤醒策略:ObjectMonitor::exit
当重量级锁持有者退出的时,调用 ObjectMonitor::exit:
1void ATTR ObjectMonitor::exit(bool markObjectIsLocked, TRAPS) { 2 Thread * Self = THREAD; 3 4 // 1. 重入计数递减 5 if (_recursions > 0) { 6 _recursions--; 7 return; 8 } 9 10 // 2. 释放 Owner 控制权 11 OrderAccess::release_store_ptr(&_owner, NULL); // 写屏障清空 _owner 12 OrderAccess::fence(); // 内存屏障保证可见性 13 14 // 3. 检查是否有等待被唤醒的线程 (_cxq 或 _EntryList 不为空) 15 if (_EntryList == NULL && _cxq == NULL) { 16 return; // 无竞争者,直接离开 17 } 18 19 // 4. 挑选唤醒节点 (Successor Selection) 20 ObjectWaiter * w = NULL; 21 22 // 根据 QMode 策略,决定是将 _cxq 转移到 _EntryList,还是直接唤醒 _cxq 栈顶 23 if (_EntryList == NULL) { 24 // 将 _cxq 整体移动到 _EntryList 变为 FIFO 顺序 25 _EntryList = _cxq; 26 _cxq = NULL; 27 } 28 29 w = _EntryList; 30 if (w != NULL) { 31 // 5. 唤醒被阻塞的继承者线程 32 Thread::SpinAcquire(&_QueueWaiter, "Monitor Exit"); 33 34 // 调用 OS 级别的 unpark 唤醒 C++ 线程 35 w->_thread->_ParkEvent->unpark(); 36 37 Thread::SpinRelease(&_QueueWaiter); 38 } 39} 40 41
7. 总结:
1、锁演进核心机制对比
| 锁类型 | 核心标志 (Mark Word) | 触发条件 | 加锁开销 | 适用场景 | 关键 HotSpot 结构 |
|---|---|---|---|---|---|
| 偏向锁 | `ThreadID | 101` | 单线程访问 | 无 CAS(仅比对 Thread ID) | 只有一个线程反复进入同步块 |
| 轻量级锁 | `BasicLock* | 00` | 多线程交替执行(无竞争) | 1 次 CAS(修改 Mark Word 指向栈帧) | 任务执行极快,交替抢锁 |
| 重量级锁 | `ObjectMonitor* | 10` | 存在激烈的多线程并发竞争 | 频繁 CAS + OS 线程上下文切换 (park/unpark) | 高并发长时临界区操作 |
2、Mark Word锁膨胀全景状态转换图
结合 OpenJDK 8源码实现,锁状态的完整转变轨迹如下:
1+---------------------------------------------------------------------------------------------------------+ 2| [新创建对象] | 3| | | 4| 开启偏向锁 (101) | 禁用偏向锁 (001) | 5| v | 6| +------------------+ | 7| | 匿名偏向 (101) | | 8| +------------------+ | 9| | | 10| 首次 CAS 获得偏向锁 | 11| v | 12| +------------------+ | 13| | 偏向锁状态(101)| | 14| +------------------+ | 15| | | 16| 竞争 / 调用 hashCode() | 17| v | 18| +-------------------+ 偏向锁撤销(Safepoint) +--------------------------+ | 19| | 无锁状态 (001) |<------------------------------------------------>| 轻量级锁 (00) | | 20| +-------------------+ +--------------------------+ | 21| | | | 22| 调用 wait() / hashCode() CAS 失败 / 存在锁竞争 | 23| | | | 24| +----------------------------> [锁膨胀过程] <-----------------------------+ | 25| | | 26| 1. omAlloc 分配 ObjectMonitor | 27| 2. CAS 将对象头 Mark Word 置为 INFLATING (0) | 28| 3. 填充 ObjectMonitor 属性 (header, owner, object) | 29| 4. CAS 将 Mark Word 设置为 ObjectMonitor* (10) | 30| v | 31| +------------------+ | 32| | 重量级锁 (10) | | 33| +------------------+ | 34+---------------------------------------------------------------------------------------------------------+ 35 36
《Java对象头Mark Word状态与锁膨胀过程剖析》 是转载文章,点击查看原文。
