JVM锁膨胀与Futex源码解析

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 源码逻辑)
    • [下沉 Linux Kernel `futex` 系统调用](#下沉 Linux Kernel futex 系统调用)
      • [操作系统抽象层:`os::PlatformEvent::park()` 源码](#操作系统抽象层:os::PlatformEvent::park() 源码)
      • [Linux 内核层:`futex` 执行链路解析](#Linux 内核层:futex 执行链路解析)
    • 系统工程师视角:开销模型与硬件级影响

前言

本文旨在记录近期研读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 块在字节码层面表现为 monitorentermonitorexit 指令。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)

系统调用与上下文切换消耗

  1. 总线锁定与 Cache Line 伪共享 (False Sharing)
    轻量级锁依赖 LOCK CMPXCHG 指令。在 x86 架构下,LOCK 前缀将触发 MESI 协议 中的 Invalid(失效)或 Modified(修改)状态切换,通过总线锁/缓存锁(Bus/Cache Locking)强行保证 CPU L1/L2 缓存一致性。大量的 CAS 失败会导致 流水线清空 (Pipeline Flush) 和缓存行频繁失效。
  2. 内核态上下文切换 (Context Switch Overhead)
    当触发重量级锁的 sys_futex 时,CPU 发生系统调用硬件中断,需要保存当前进程的通用寄存器(RAX, RBX, RCX, RSP 等)、指令指针(RIP)及段寄存器到 Kernel Stack,并将 CPU 模式从 Ring 3 (User) 切换至 Ring 0 (Kernel)。
  3. TLB 与 CPU Cache 污染
    线程被 schedule() 切出后,内核调度器载入新线程的页表基址(CR3 寄存器)。即便在新内核路径中未完全刷新 TLB,当前 CPU 的 L1/L2 Cache 中的数据也会在后续运行中被其他线程冲刷,导致主线程唤醒后遭遇大量 L1/L2/L3 Cache MissPage Walk 开销,单次重量级锁的挂起与唤醒综合延迟通常达到几微秒级别。
相关推荐
郝学胜-神的一滴41 分钟前
C++11 工程级应用 08:Lambda表达式与Tuple元组
开发语言·jvm·c++·python·程序人生·开源
CoderYanger1 小时前
A.每日一题:3622. 判断整除性
java·程序人生·算法·leetcode·面试·职场和发展·蓝桥杯
斑马1391 小时前
Linux软件编程学习笔记(十一)——HTTP协议
linux·笔记·学习
吴声子夜歌1 小时前
ApacheCommons——commons-lang3(Java 基础语言增强)(二)
java·开发语言·apache
xcl09251 小时前
幼儿托管系统开发实战:从需求分析到部署上线全指南
java·spring boot·需求分析
OpenPomeloxCommunity1 小时前
Linux驱动基础(一):模块机制的设计与实现
linux
我不会起名字3221 小时前
一天一道算法题(26):栈的简单应用
java·数据结构·python·算法·leetcode·golang·
susplus1 小时前
【HTTP协议】HTTP介绍及基础【C语言爬虫实现】
c语言·爬虫·http·万维网
Shadow(⊙o⊙)1 小时前
Linux网络——文件与网络的连接桥梁struct sock {
linux·运维·网络