HotSpot轻量级锁thin-lock实现中lock record生命周期相关问题咨询
问题1:为什么每次尝试进入锁时都要新建一个lock record?为每个对象仅保留一个lock record不是更优吗?
这种设计是HotSpot轻量级锁在无竞争场景下追求极致性能的选择,核心原因有三点:
- 完全复用线程栈的生命周期,无额外存储开销。
synchronized的加解锁逻辑天然和方法栈帧的入栈/出栈绑定,lock record直接分配在当前栈帧上,不需要额外在堆上分配存储,也不需要维护线程和锁关联的独立数据结构,分配和销毁完全跟随栈帧自动完成,没有额外GC负担。如果为每个对象单独维护lock record,不仅要占用对象头之外的额外内存,还要额外维护重入计数、持有线程关联等元数据,性能开销远高于栈上分配。 - 天然匹配锁的嵌套结构,不需要额外维护加锁顺序。
synchronized是可重入的,且加解锁顺序严格遵循先进后出的栈规则,栈上按加锁顺序依次压入的lock record天然对应重入的层级,不需要额外维护链表、计数顺序等逻辑,运行时效率更高。 - 避免额外的原子操作开销。如果只用一个lock record存储重入计数,每次重入都要修改计数的值,即便没有竞争也要做变量更新。而多lock record的设计,重入时只需要在当前栈帧压入一个值为0的record即可,不需要修改之前的任何数据,完全无写冲突。
问题2:退出synchronized代码块时,VM如何判断是需要直接释放锁,还是仍在递归锁的"解套"过程中?
判断逻辑完全依托栈上lock record的存储值,流程非常简单:
- 执行
monitorexit时,首先取当前栈顶对应本次加锁操作的lock record,检查其存储的displaced header值:- 如果值为0,说明这个record是重入加锁时创建的,当前还处于递归解套阶段,直接弹出这个lock record即可,不需要修改对象头,也不需要执行释放逻辑,相当于重入次数减1。
- 如果值不为0,说明这是第一次加锁时创建的最底层lock record,已经到了解套的最后一层。
- 对值不为0的lock record,会尝试用存储的原始
displaced headerCAS替换对象头的当前值:- CAS成功说明锁持有期间没有其他线程竞争,直接替换完成后锁回到无锁状态,释放完成。
- CAS失败说明锁持有期间已经发生过竞争,膨胀为了重量级锁,此时会进入慢路径,关联到对应的monitor结构,将monitor的持有计数减1,计数归0时唤醒等待的竞争线程。
内容的提问来源于stack exchange,提问作者devoured elysium
相关产品推荐
相关产品推荐

