偏向锁撤销为什么会触发STW?

偏向锁撤销触发STW(Stop-The-World)的根本原因在于其撤销操作需要​​全局内存一致性​​和​​线程状态确定性​​,具体机制如下:


⚙️ ​​一、偏向锁撤销的核心流程​

  1. ​竞争触发撤销​
    当线程B尝试获取已被线程A偏向的锁时,JVM检测到竞争,触发撤销操作。
  2. ​挂起持有线程​
    需暂停持有偏向锁的线程A,检查其是否仍在同步块中:
    • 若线程A已退出同步块:直接撤销偏向锁,恢复为无锁状态(标志位01)。
    • 若线程A仍在同步块中:升级为轻量级锁(标志位00),线程B继续竞争。
  3. ​对象头修改​
    将对象头(Mark Word)中的线程ID移除,替换为轻量级锁指针或无锁状态。

🛑 ​​二、STW的必要性​

撤销操作必须在​​全局安全点(Safepoint)​​ 进行,原因如下:

  1. ​线程状态一致性​
    需确保所有线程(尤其是持有偏向锁的线程)处于可安全挂起的状态(如未执行字节码),防止撤销过程中线程修改对象头或栈帧。
  2. ​线程栈遍历需求​
    判断线程A是否在同步块中,需遍历其栈帧中的Lock Record(锁记录)。若线程未暂停,栈帧可能动态变化,导致数据不一致。
  3. ​内存屏障要求​
    对象头修改需原子性,且其他线程必须立即可见。STW期间可避免CPU缓存与主存不一致问题。

⏱️ ​​三、STW的性能影响​

  • ​短暂停顿​​:单次撤销通常耗时​​微秒级​​(如0.1-1ms),对低竞争场景影响小。
  • ​高并发瓶颈​​:频繁竞争会导致多次撤销,累积STW时间显著增加(如百线程竞争时可达毫秒级)。
  • ​日志示例​​:
    启用安全点日志(-XX:+PrintSafepointStatistics)可见:
    RevokeBias [threads: 200 initially_running: 5] 
    [time: spin=0.1ms block=2ms cleanup=0.5ms vmop=15ms]
    其中vmop=15ms为实际STW时间。

⚖️ ​​四、不同场景下的撤销处理​

​场景​处理方式是否需STW
持有线程已退出同步块直接恢复为无锁状态是(但操作更快)
持有线程仍在同步块中升级为轻量级锁
调用hashCode()/wait()强制撤销并升级重量级锁

调用hashCode()会覆盖Mark Word中的线程ID,强制撤销偏向锁。


🚀 ​​五、优化建议​

  1. ​禁用偏向锁​
    高并发场景(如秒杀系统)通过 -XX:-UseBiasedLocking 禁用,避免频繁撤销导致的STW。
  2. ​监控安全点​
    临时开启日志定位非GC导致的STW:
    -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1
  3. ​升级JDK版本​
    JDK 15+ 默认关闭偏向锁,JDK 18+ 已移除该机制,彻底规避此问题。

💎 ​​总结​

偏向锁撤销触发STW的本质是​​保证内存与线程状态一致性​​的代价。在单线程场景中,偏向锁通过避免CAS操作提升性能;但在多线程竞争下,其撤销机制反而成为瓶颈。​​高并发系统应禁用偏向锁​​,选择轻量级锁(CAS)或直接使用java.util.concurrent中的并发工具(如ReentrantLock),以消除STW对延迟的影响。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值