synchronized 的重入计数器存于堆中 objectmonitor 的 _recursions 字段,类型为有符号整型,无逻辑上限但受栈空间和数值范围限制,由 jvm 在 monitorenter/monitorexit 时自动增减,java 层不可访问或修改。

synchronized 本身没有显式的“重入次数限制”,Java 规范和 HotSpot 实现中均未设定硬性上限(比如不能超过 100 层)。它的重入能力是通过对象监视器(ObjectMonitor)中的一个整型计数器(_recursions)实现的,该计数器记录当前持有锁的线程已递归进入同步块/方法的次数。
重入计数器存在哪里?
在 HotSpot JVM 中,每个 Java 对象头(mark word)在被锁住后,会指向一个堆上的 ObjectMonitor 结构。这个结构由 C++ 实现,其中关键字段包括:
-
_owner:指向当前持有锁的线程(Thread*) -
_recursions:intptr_t类型(通常为 4 或 8 字节),记录重入深度,初始为 0,每次重入 +1,退出同步块时 -1 -
_entry_list和_wait_set:管理等待获取锁或调用wait()的线程队列
也就是说,计数器不存于对象头或栈帧中,而是维护在 JVM 堆内与该对象关联的 ObjectMonitor 实例里。
重入次数实际上受什么约束?
虽然无逻辑限制,但实际重入深度受限于:
-
栈空间:每次方法调用(含 synchronized 方法)都会消耗栈帧。无限递归会导致
StackOverflowError,这比计数器溢出早得多 -
_recursions 字段的数值范围:它是有符号整型(如
int或intptr_t),理论最大值约 2³¹−1(21 亿)。但现实中不可能达到——在到达前早已因栈溢出崩溃 -
ObjectMonitor 分配失败:极端情况下(如大量对象争用、内存耗尽),创建或复用
ObjectMonitor可能失败,但这属于资源异常,不是重入机制本身的限制
底层如何更新计数器?
HotSpot 在字节码解释执行和 JIT 编译后代码中,对 monitorenter / monitorexit 指令做了特殊处理:
- 执行
monitorenter时:先检查_owner == current_thread;若相等,则直接_recursions++,跳过锁竞争流程 - 执行
monitorexit时:若_recursions > 0,则_recursions--;仅当减到 0 时,才真正释放锁(置_owner = null,唤醒_entry_list中线程) - 这一过程由 JVM 内部原子操作保障线程安全,不依赖 Java 层面的同步
可否查看或修改这个计数器?
不能。该计数器完全由 JVM 内部维护,Java 语言层无任何 API 暴露或干预入口。调试时可通过 Unsafe.monitorEnter/monitorExit(非公开 API)或 JVMTI 工具间接观察锁状态,但无法读写 _recursions 值。试图绕过 JVM 直接操作 ObjectMonitor 会导致未定义行为。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











