monitorenter仅是申请锁的触发指令,不暴露锁状态;实际锁态(偏向/轻量级/重量级)由jvm运行时检查对象头mark word并调用fast_enter或slow_enter决定,需结合jol查看mark word低3位及native地址确认。

monitorenter 指令本身不暴露 ObjectMonitor 状态,它只是触发点
你不能从 monitorenter 这条字节码指令里直接读出 Monitor 是偏向锁、轻量级还是重量级——它只是一个 JVM 规范定义的“申请锁”动作。真正决定状态切换的是运行时 JVM 对目标对象头(mark word)的检查 + ObjectSynchronizer::fast_enter 或 slow_enter 的调用路径。所以想分析状态,得结合对象头内容和 JIT 日志,而不是盯着字节码数指令。
javap -v 看到 monitorenter 后,下一步该查对象头布局
光有 monitorenter 只说明这里用了 synchronized 块,不代表锁一定升级了。要确认当前锁状态,必须看这个锁对象在堆里的实际 mark word 值:
- 用 JOL(
org.openjdk.jol:jol-cli)运行java -jar jol-cli.jar internals YourClass,观察对象头中 mark word 的二进制位 - 重点关注低 3 位:001=无锁态,101=偏向锁,000=轻量级锁(指向栈中 lock record),010=重量级锁(指向堆中
ObjectMonitor) - 如果看到 mark word 指向 native 内存地址(比如 0x00000007xxxxxx00),说明已膨胀为重量级锁,
ObjectMonitor已分配
monitorenter 执行时的三类典型路径对应不同 ObjectMonitor 行为
monitorenter 指令执行后,JVM 底层会走不同分支,每种都影响 ObjectMonitor 是否被创建、是否被复用:
- 偏向锁路径(
UseBiasedLocking=true且未撤销):不分配ObjectMonitor,只改 mark word,_owner字段为空;此时monitorenter几乎无竞争开销 - 轻量级锁路径(CAS 替换 mark word 成栈帧中的 lock record):仍不触发
ObjectMonitor分配,_owner也不设值;失败则立即进入膨胀流程 - 重量级锁路径(
slow_enter):调用ObjectSynchronizer::inflate分配并初始化ObjectMonitor,设置_owner = current thread,_count = 1,_recursions = 0
别指望通过 monitorenter 数量判断锁竞争激烈程度
一个方法里出现多次 monitorenter,可能只是多个同步块,也可能被 JIT 优化合并;而 monitorenter 执行失败次数根本不会反映在字节码里。真正要看竞争,得开 JVM 参数:
-
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps配合锁膨胀日志(需额外加-XX:+UnlockDiagnosticVMOptions -XX:+PrintBiasedLockingStatistics) -
-XX:+TraceClassLoading和-XX:+LogCompilation能帮你定位哪些方法触发了锁膨胀 - 注意:JIT 编译后,
monitorenter可能被内联或消除,javap -v看到的只是解释执行阶段的原始字节码
ObjectMonitor 不是 Java 对象,不占堆内存,它的生命周期由 JVM 在 native 内存中管理;一旦分配,就和那个对象强绑定,直到 GC 回收该对象时才释放。这点容易被忽略——你看到 monitorenter 成功,并不意味着 Monitor 正在被频繁争抢,更不意味着它一定还活着。











