JVM锁膨胀与Futex源码解析
- 前言
- JVM锁膨胀与Futex源码解析
-
- [Mark Word 与锁状态位域布局](#Mark Word 与锁状态位域布局)
- [锁膨胀流程与 HotSpot 入口分析](#锁膨胀流程与 HotSpot 入口分析)
- [核心膨胀函数 `ObjectSynchronizer::inflate` 源码全解](#核心膨胀函数
ObjectSynchronizer::inflate源码全解) - [重量级锁 HotSpot `ObjectMonitor` 内部机制](#重量级锁 HotSpot
ObjectMonitor内部机制) -
- [`ObjectMonitor` 核心字段结构](#
ObjectMonitor核心字段结构) - [竞争获取:`ObjectMonitor::enter` 源码逻辑](#竞争获取:
ObjectMonitor::enter源码逻辑) - [释放与唤醒:`ObjectMonitor::exit` 源码逻辑](#释放与唤醒:
ObjectMonitor::exit源码逻辑)
- [`ObjectMonitor` 核心字段结构](#
- [下沉 Linux Kernel `futex` 系统调用](#下沉 Linux Kernel
futex系统调用) -
- [操作系统抽象层:`os::PlatformEvent::park()` 源码](#操作系统抽象层:
os::PlatformEvent::park()源码) - [Linux 内核层:`futex` 执行链路解析](#Linux 内核层:
futex执行链路解析)
- [操作系统抽象层:`os::PlatformEvent::park()` 源码](#操作系统抽象层:
- 系统工程师视角:开销模型与硬件级影响
前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。
JVM锁膨胀与Futex源码解析
Mark Word 与锁状态位域布局
Java 对象的同步语义依赖于对象头中的 Mark Word。在 64 位 HotSpot 虚拟机中(开启与未开启指针压缩 +UseCompressedOops 模式下,Mark Word 均为 64 位),Mark Word 的低 3 位控制了锁的状态编码:
|--------------------------------------------------------------------------------------------------------------|--------------------|
| Object Header Mark Word (64 bits) | State |
|--------------------------------------------------------------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:0 | lock:01 | Unlocked (无锁) |
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:01 | Biased (偏向锁) |
| ptr_to_lock_record:62 | lock:00 | Lightweight (轻量锁)|
| ptr_to_object_monitor:62 | lock:10 | Heavyweight (重量锁)|
| marked_for_gc:62 | lock:11 | GC Mark (GC标记) |
|--------------------------------------------------------------------------------------------------------------|--------------------|
以下为 HotSpot 源码 src/share/vm/oops/markOop.hpp 中对状态位的定义与计算掩码:
cpp
// src/share/vm/oops/markOop.hpp
class markOopDesc : public ooDesc {
private:
// 位偏移量及长度常量定义
enum { age_bits = 4,
lock_bits = 2,
biased_lock_bits = 1,
max_hash_bits = 31,
hash_bits = 31,
epoch_bits = 2
};
// 状态掩码与标志位
enum { lock_shift = 0,
biased_lock_shift = lock_bits, // Shift = 2
age_shift = lock_bits + biased_lock_bits, // Shift = 3
hash_shift = age_shift + age_bits, // Shift = 7
epoch_shift = hash_shift + hash_bits // Shift = 38
};
enum { lock_mask = right_n_bits(lock_bits), // 0b11
lock_mask_in_place = lock_mask << lock_shift,
biased_lock_mask = right_n_bits(biased_lock_bits), // 0b1
biased_lock_mask_in_place= biased_lock_mask << biased_lock_shift,
biased_lock_bit_in_place = 1 << biased_lock_shift, // 0b100 (第3位)
age_mask = right_n_bits(age_bits),
hash_mask = right_n_bits(hash_bits)
};
enum { locked_value = 0, // 00: 轻量级锁
unlocked_value = 1, // 01: 无锁状态 (biased_lock=0)
monitor_value = 2, // 10: 重量级锁 (ObjectMonitor指针)
marked_value = 3, // 11: GC 标记
biased_lock_pattern = 5 // 101: 偏向锁标记 (biased_lock=1, lock=01)
};
public:
// 判定当前对象锁状态的内部内联函数
bool is_locked() const { return (mask_bits(value(), lock_mask_in_place) != unlocked_value); }
bool is_unlocked() const { return (mask_bits(value(), lock_mask_in_place) == unlocked_value); }
bool has_bias_pattern() const {
return (mask_bits(value(), lock_mask_in_place | biased_lock_mask_in_place) == biased_lock_pattern);
}
bool has_locker() const { return ((value() & lock_mask_in_place) == locked_value); }
bool has_monitor() const { return ((value() & lock_mask_in_place) == monitor_value); }
// 获取指向栈上 BasicLock 的指针或指向堆外 ObjectMonitor 的指针
BasicLock* locker() const {
assert(has_locker(), "check");
return (BasicLock*) value(); // 清除低2位00后直接转换位指针
}
ObjectMonitor* monitor() const {
assert(has_monitor(), "check");
return (ObjectMonitor*) (value() ^ monitor_value); // 清除低2位10,提取物理内存地址
}
};
锁膨胀流程与 HotSpot 入口分析
Java 编译后的 synchronized 块在字节码层面表现为 monitorenter 和 monitorexit 指令。HotSpot 在解释器(Template Interpreter)与 C1/C2 编译器中均提供了快速路径(Fast-path)和慢速路径(Slow-path)。
+------------------+ CAS 偏向当前线程成功 +------------------+
| Unlocked (001) | ---------------------------> | Biased (101) |
+------------------+ +------------------+
| |
| CAS 替换 Lock Record (无竞争) | 存在线程竞争 / 调用 hashCode()
v v
+------------------+ 多线程交替执行/存在轻微竞争 +------------------+
| Lightweight(000) | <--------------------------- | Revoke Bias |
+------------------+ +------------------+
|
| 存在高并发竞争 / 线程自旋失败
v
+--------------------------------------------------------------------+
| Heavyweight (010) [ObjectSynchronizer::inflate() 分配 ObjectMonitor] |
+--------------------------------------------------------------------+
慢速路径入口:ObjectSynchronizer::fast_enter
cpp
// src/share/vm/runtime/synchronizer.cpp
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
if (UseBiasedLocking) {
if (!SafetyPointSynchronize::is_at_safepoint()) {
// 偏向锁逻辑:尝试获取偏向锁或撤销偏向锁
BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
return; // 成功重偏向至当前线程,直接返回
}
} else {
BiasedLocking::revoke_at_safepoint(obj);
}
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
// 偏向锁失效或禁用时,进入轻量级锁逻辑
slow_enter(obj, lock, THREAD);
}
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
assert(!mark->has_bias_pattern(), "focus on non-biased locking path");
if (mark->is_unlocked()) {
// ----------------------------------------------------
// [无锁 -> 轻量级锁] 路径
// ----------------------------------------------------
// 1. 将对象的当前 Mark Word 存入线程栈帧中的 BasicLock (Displaced Mark Word)
lock->set_displaced_header(mark);
// 2. 使用 CAS 将对象头的 Mark Word 替换为指向栈上 BasicLock 的指针
// expected: mark (无锁状态 001)
// new_val: lock (指向栈帧 BasicLock,低位为 00)
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj->mark_addr(), mark)) {
TEVENT (slow_enter: fast stacklock);
return; // CAS 成功,拿到轻量级锁
}
// CAS 失败,说明存在竞争,向下落入膨胀逻辑
} else if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
// ----------------------------------------------------
// [轻量级锁重入] 路径
// ----------------------------------------------------
// 锁已由当前线程持有,将 Displaced Mark Word 设为 NULL 表示重入
assert(lock != mark->locker(), "must not be similar to BasicLock");
assert(lock->displaced_header() == NULL, "must be null");
lock->set_displaced_header(NULL);
return;
}
// ----------------------------------------------------
// [轻量级锁 -> 重量级锁] 膨胀路径
// ----------------------------------------------------
// 重置 BasicLock 的 Displaced Header,准备向重量级锁过渡
lock->set_displaced_header(markOopDesc::unused_mark());
// 调用 inflate 进行膨胀,并返回对应的 ObjectMonitor 指针,然后执行 enter
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
核心膨胀函数 ObjectSynchronizer::inflate 源码全解
锁膨胀是一个状态机转换过程,inflate() 函数必须处理多线程同时触发膨胀的并发竞态条件(Race Condition)。
cpp
// src/share/vm/runtime/synchronizer.cpp
ObjectMonitor* ObjectSynchronizer::inflate(Thread * Self, ooDesc * object) {
// 保证内存访问的自旋死循环
for (;;) {
const markOop mark = object->mark();
assert(!mark->has_bias_pattern(), "invariant");
// ===================================================================
// CASE 1: 已经是重量级锁 (Mark Word 低两位为 10)
// ===================================================================
if (mark->has_monitor()) {
ObjectMonitor * monitor = mark->monitor();
assert(monitor->object() == object, "invariant");
return monitor; // 膨胀已完成,直接返回堆外 ObjectMonitor
}
// ===================================================================
// CASE 2: 正在膨胀中 (膨胀中间态:Mark Word 为 0)
// ===================================================================
if (mark == markOopDesc::INFLATING()) {
// 另一个线程正在执行膨胀流程,当前线程自旋等待其完成
ReadStableMark(object); // 内部执行 spin/yield 等待 mark 改变
continue;
}
// ===================================================================
// CASE 3: 当前为轻量级锁 (Mark Word 低两位为 00)
// ===================================================================
if (mark->has_locker()) {
// 1. 分配一个空闲的 ObjectMonitor 结构体 (优先从线程本地 InUse 列表获取)
ObjectMonitor * m = omAlloc(Self);
m->Recycle();
m->_knob0 = 0;
m->_Owner = NULL;
m->_object = object;
m->_recursions = 0;
m->_SpinDuration = ObjectMonitor::Knob_SpinLimit;
// 2. 使用 CAS 将对象头 Mark Word 设置为 INFLATING (0x0) 临界标记位
markOop cmp = (markOop) Atomic::cmpxchg_ptr(markOopDesc::INFLATING(), object->mark_addr(), mark);
if (cmp != mark) {
omRelease(Self, m); // CAS 失败,存在竞态,释放已分配的 Monitor 并重试
continue;
}
// 3. CAS 抢占成功,当前线程独占膨胀控制权,开始初始化 ObjectMonitor 的内部状态
// 读取原持有轻量级锁线程在栈帧中保存的 Displaced Mark Word
markOop dmw = mark->locker()->displaced_header();
assert(dmw->is_unlocked(), "invariant");
// 设置 Monitor 参数
m->set_header(dmw); // 保存原始的无锁状态 Mark Word
// 设置 _owner 为原栈上 BasicLock 的持有者(注意:此时尚未修改为当前线程,因为锁原先被别人持有)
m->set_owner(mark->locker());
m->_object = object;
// 4. 【关键节点】使用 Release 内存屏障将 ObjectMonitor 指针写入对象头 Mark Word
// 同时将其状态改为重量级锁标记 (010)
object->release_set_mark(markOopDesc::encode(m));
// 5. 膨胀成功,返回重量级锁实例
return m;
}
// ===================================================================
// CASE 4: 当前为无锁状态 (Mark Word 低两位为 01)
// ===================================================================
assert(mark->is_unlocked(), "invariant");
ObjectMonitor * m = omAlloc(Self);
m->Recycle();
m->set_header(mark); // 保存无锁 Mark Word
m->set_owner(NULL); // 无锁状态下 owner 为空
m->_object = object;
m->_recursions = 0;
// CAS 直接将 Mark Word 替换为重量级锁指针 (encode 过程将指针低2位置为 10)
if (Atomic::cmpxchg_ptr(markOopDesc::encode(m), object->mark_addr(), mark) != mark) {
m->set_object(NULL);
omRelease(Self, m);
continue; // CAS 冲突,重试
}
return m;
}
}
重量级锁 HotSpot ObjectMonitor 内部机制
当锁膨胀为重量级锁后,线程的抢占、等待与唤醒全面交由 ObjectMonitor 管理。
ObjectMonitor 核心字段结构
ObjectMonitor 对象的物理内存位于 Native Heap(非 JVM Heap)中:
cpp
// src/share/vm/runtime/objectMonitor.hpp
class ObjectMonitor {
private:
friend class ObjectSynchronizer;
markOop _header; // 保存对象原始的 Mark Word (Displaced Mark Word)
void* volatile _object; // 指向关联的 Java 对象物理地址
void* volatile _owner; // 指向持有该锁的线程 (Thread* 指针) 或 栈上 BasicLock 指针
intptr_t _recursions; // 锁重入计数器 (0 表示未重入)
ObjectWaiter* volatile _cxq; // 多线程竞争入队的单向链表头指针 (Contention Queue)
ObjectWaiter* volatile _EntryList;// 拥有获取锁资格的等待线程双向链表
ObjectWaiter* volatile _WaitSet; // 调用 Object.wait() 后挂起的线程链表
volatile intptr_t _count; // 等待或阻塞在此 Monitor 上的线程总数
volatile int _waiters; // waitSet 中的线程数量
};
ObjectMonitor 内部数据流转:
+-----------------------+
| Thread Lock Request |
+-----------------------+
|
v
+-----------------------+
| CAS Acquire _owner | --(Success)--> [Get Lock]
+-----------------------+
| (Failure)
v
+-----------------------+
| Adaptive Spinning | --(Success)--> [Get Lock]
+-----------------------+
| (Failure)
v
+-----------------------+
| Lock-free Push to _cxq| <--- 竞争队列 (Stack-like)
+-----------------------+
|
v
+-----------------------+
| park() & Wait Loop | <--- 下沉 Linux futex 挂起
+-----------------------+
^
| Unpark
|
+-------------+ Wakes +-----------------------+
| ObjectExit | ---------> | _EntryList / _cxq Node|
+-------------+ +-----------------------+
竞争获取:ObjectMonitor::enter 源码逻辑
cpp
// src/share/vm/runtime/objectMonitor.cpp
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD;
void * cur;
// 1. CAS 尝试将 _owner 从 NULL 设置为当前线程
cur = Atomic::cmpxchg_ptr(Self, &_owner, NULL);
if (cur == NULL) {
return; // 抢占成功,直接返回
}
// 2. 处理重入情况:如果 _owner 指向的就是当前线程
if (cur == Self) {
_recursions++;
return;
}
// 3. 当前线程是轻量级锁的持有者(说明膨胀前锁由当前线程持有)
if (Self->is_lock_owned((address)cur)) {
assert(_recursions == 0, "internal invariant");
_recursions = 1;
_owner = Self; // 将 owner 修正为当前 Thread 指针
return;
}
// 4. 真正存在竞争,准备进入自旋与阻塞流程
Self->_Stalled = intptr_t(this);
// 尝试自适应自旋 (Adaptive Spinning)
if (Knob_SpinLimit > 0) {
if (TrySpin(Self) > 0) {
Self->_Stalled = 0;
return; // 自旋成功获取锁,避免了内核态切换
}
}
// 5. 自旋失败,将线程构造为 ObjectWaiter 节点入队
ObjectWaiter node(Self);
Self->_ParkEvent->reset();
node._status = ObjectWaiter::TS_CXQ;
// 6. 无锁 CAS 入队到 _cxq 链表头部
ObjectWaiter * cmp;
ObjectWaiter * q;
for (;;) {
node._next = q = _cxq;
if ((cmp = (ObjectWaiter *) Atomic::cmpxchg_ptr(&node, &_cxq, q)) == q) {
break; // CAS 入队成功
}
}
// 7. 阻塞循环 (Park Loop)
for (;;) {
if (TryLock(Self) > 0) break; // 再次尝试 CAS 获取锁
// 依然获取失败,进入挂起流程下沉至 OS 系统调用
if (node._status == ObjectWaiter::TS_CXQ) {
// 真正执行 Thread Park (阻塞线程)
Self->_ParkEvent->park();
}
// 线程被 unpark 唤醒后,在此循环中重新争抢 _owner
}
Self->_Stalled = 0;
}
释放与唤醒:ObjectMonitor::exit 源码逻辑
cpp
// src/share/vm/runtime/objectMonitor.cpp
void ATTR ObjectMonitor::exit(bool lock_held_by_custom_code, TRAPS) {
Thread * Self = THREAD;
if (THREAD != _owner) {
if (THREAD->is_lock_owned((address)_owner)) {
// 轻量级锁所有者释放重量级锁时的 owner 修正
_owner = THREAD;
_recursions = 0;
} else {
// 非法的锁释放状态
TEVENT(Exit - IllegalMonitorStateException);
THROW(vmSymbols::java_lang_IllegalMonitorStateException());
}
}
if (_recursions > 0) {
_recursions--; // 重入次数递减
TEVENT(Inflated exit - recursive);
return;
}
// 释放锁前,将 _owner 清空,使得后续自旋线程可以抢占
OrderAccess::release_store_ptr(&_owner, NULL);
OrderAccess::fence(); // 内存屏障,强制刷新 Store Buffer,保证可见性
// 如果 _cxq 和 _EntryList 均为空,说明没有等待者,直接退出
if ((intptr_t(_cxq) | intptr_t(_EntryList)) == 0) {
return;
}
// 根据策略(Knob_QMode)从 _EntryList 或 _cxq 中选择继承人 (Successor)
ObjectWaiter * w = NULL;
// 默认策略:若 _EntryList 为空,将 _cxq 链表反转并转移至 _EntryList
if (_EntryList == NULL) {
assert(_cxq != NULL, "invariant");
_EntryList = DrainQueue(); // 弹出 _cxq
}
w = _EntryList;
if (w != NULL) {
// 唤醒继承线程
Thread::SpinAcquire(&_metadataLock, "WaitSetLock");
// 调用底层的 unpark 唤醒 target 线程
w->_thread->_ParkEvent->unpark();
Thread::SpinRelease(&_metadataLock);
}
}
下沉 Linux Kernel futex 系统调用
JVM 中的 Self->_ParkEvent->park() 并不是一个简单的用户态标记,它是重量级锁引发操作系统上下文切换的关键下沉点。
操作系统抽象层:os::PlatformEvent::park() 源码
在 Linux 平台上,HotSpot 通过 os_linux.cpp 中的 PlatformEvent 实现线程挂起。具体机制依赖于 glibc 的 pthread_mutex / pthread_cond 或直接基于系统调用 sys_futex。
cpp
// src/os/linux/vm/os_linux.cpp
void os::PlatformEvent::park() {
// 1. 原子检查标识,若已有 grant 标记,直接消费并返回(避免不必要的系统调用)
if (Atomic::xchg(0, &_Event) > 0) return;
// 获取 OS 线程锁
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
// 2. 状态校验设置
guarantee(_nParked == 0, "invariant");
++_nParked;
// 3. 循环挂起逻辑,防止伪唤醒 (Spurious Wakeups)
while (_Event <= 0) {
// 调用 POSIX 线程库条件变量等待,底层即下沉至 Linux futex 系统调用
status = pthread_cond_wait(_cond, _mutex);
}
--_nParked;
_Event = 0;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
}
Linux 内核层:futex 执行链路解析
当 JVM 执行 pthread_cond_wait 时,glibc 内部将封装 sys_futex 系统调用陷入内核态。
[User Space] Java Thread -> ObjectMonitor::enter() -> os::PlatformEvent::park()
|
pthread_cond_wait() / glibc
|
============================================================| System Call (sys_futex)
[Kernel Space] v
SYSCALL_DEFINE6(futex...)
|
do_futex()
|
futex_wait()
|
queue_me() -> schedule()
|
[Thread Marked TASK_INTERRUPTIBLE]
[CPU Context Saved & Switched]
Linux 内核中的 futex(Fast Userspace Mutex)核心机制分析如下:
c
// linux/kernel/futex.c (Linux 内核源码示意)
// 系统调用入口
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
struct timespec __user *, utime, u32 __user *, uaddr2, u32, val3)
{
return do_futex(uaddr, op, val, timeout, uaddr2, val3);
}
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, ...)
{
int cmd = op & FUTEX_CMD_MASK;
switch (cmd) {
case FUTEX_WAIT:
// 当前用户态地址 uaddr 的值若仍等于 val,则当前进程/线程入队挂起
return futex_wait(uaddr, flags, val, timeout, val3);
case FUTEX_WAKE:
// 唤醒挂起在 uaddr 哈希桶队列上的 val 个线程
return futex_wake(uaddr, flags, val, val3);
}
}
static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time)
{
struct futex_hash_bucket *hb;
struct futex_q q = FUTEX_Q_INIT;
// 1. 获取物理页映射并计算内核态哈希桶 (Hash Bucket)
// 根据 uaddr 虚拟地址在内核中匹配对应的全局 futex_hash_bucket
ret = get_futex_key(uaddr, flags & FUTEX_PRIVATE_FLAG, &q.key, VERIFY_READ);
hb = hash_futex(&q.key);
// 2. 校验原子值,防止竞态条件:再次检查用户态内存 (*uaddr) 是否等于期望值 val
// 如果用户态内存已经被其他线程修改,直接返回 -EWOULDBLOCK,不陷入阻塞
u32 uval;
ret = futex_atomic_cmpxchg_inatomic(&uval, uaddr, val);
if (uval != val) {
return -EWOULDBLOCK;
}
// 3. 将当前线程放入哈希桶的等待双向链表中
queue_me(&q, hb);
// 4. 调用内核调度器,切出当前 CPU 核心的上下文,将进程状态变为 TASK_INTERRUPTIBLE
set_current_state(TASK_INTERRUPTIBLE);
schedule(); // 触发内核上下文切换 (Context Switch)
// ----------------------------------------------------
// 被 futex_wake 唤醒后从此处恢复执行
// ----------------------------------------------------
return 0;
}
系统工程师视角:开销模型与硬件级影响
锁状态转变的系统级性能与影响对照
| 维度 | 无锁 (Lock-Free) | 偏向锁 (Biased Locking) | 轻量级锁 (Lightweight) | 重量级锁 (Heavyweight) |
|---|---|---|---|---|
| 物理位置 | Mark Word (001) | Mark Word (101) | Mark Word (000) -> 栈帧 BasicLock |
Mark Word (010) -> 堆外 ObjectMonitor |
| 核心机制 | 无同步机制 | 标记 Thread ID, 无 CAS 抢占 | 用户态 LOCK CMPXCHG 指令 |
OS 系统调用 futex_wait/futex_wake |
| 内存开销 | 0 额外开销 | 0 额外开销 | 栈上开销(Displaced Mark Word) |
C++ Native Heap 分配 ObjectMonitor |
| 锁竞争成本 | 0 | 低(仅撤销时触发 SafePoint) | 中(硬件总线锁/自旋 CPU 消耗) | 高(内核态切换、CPU Cache Invalidation) |
| 单次耗时 | ~0 ns | ~0.5 ns | ~10-15 ns | ~1000-2000 ns ( 1 − 2 μ s 1-2\ \mu\text{s} 1−2 μs) |
系统调用与上下文切换消耗
- 总线锁定与 Cache Line 伪共享 (False Sharing) :
轻量级锁依赖LOCK CMPXCHG指令。在 x86 架构下,LOCK前缀将触发 MESI 协议 中的 Invalid(失效)或 Modified(修改)状态切换,通过总线锁/缓存锁(Bus/Cache Locking)强行保证 CPU L1/L2 缓存一致性。大量的 CAS 失败会导致 流水线清空 (Pipeline Flush) 和缓存行频繁失效。 - 内核态上下文切换 (Context Switch Overhead) :
当触发重量级锁的sys_futex时,CPU 发生系统调用硬件中断,需要保存当前进程的通用寄存器(RAX, RBX, RCX, RSP 等)、指令指针(RIP)及段寄存器到 Kernel Stack,并将 CPU 模式从 Ring 3 (User) 切换至 Ring 0 (Kernel)。 - TLB 与 CPU Cache 污染 :
线程被schedule()切出后,内核调度器载入新线程的页表基址(CR3 寄存器)。即便在新内核路径中未完全刷新 TLB,当前 CPU 的 L1/L2 Cache 中的数据也会在后续运行中被其他线程冲刷,导致主线程唤醒后遭遇大量 L1/L2/L3 Cache Miss 和 Page Walk 开销,单次重量级锁的挂起与唤醒综合延迟通常达到几微秒级别。