商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 对象的 hashcode 存储:分析为什么调用了默认 hashCode() 的对象无法再进入偏向锁状态

对象的 hashcode 存储:分析为什么调用了默认 hashCode() 的对象无法再进入偏向锁状态

  发布于2026-07-08 阅读(0)

扫一扫,手机访问

你有没有想过,为什么一个对象一旦调用了默认的 `hashCode()` 方法,就再也无法进入偏向锁状态?这背后不是简单的性能取舍,而是一个底层数据布局的硬性冲突——根本原因在于对象头的 **mark word** 中,hashcode 和偏向锁信息共享同一块存储空间,二者天然互斥。调用默认 `hashCode()` 时,JVM 会一次性将 identity hashcode 写入对象头,覆盖掉原本用于存放线程 ID 的位置,后续自然也就无法再进入偏向状态了。

对象的 hashcode 存储:分析为什么调用了默认 hashCode() 的对象无法再进入偏向锁状态

### 对象头 mark word 的空间争夺 在 HotSpot JVM 中,64 位平台下 mark word 总共 64 位(32 位平台则为 32 位),其中用于记录锁状态和元数据的低位字段是有限且复用的。具体来说: - 无锁状态时,mark word 会留出 31 位(或 62 位)空间专门存放 identity hashcode 值; - 偏向锁启用后,JVM 需要用这同一段空间来存入线程 ID(54 位)、epoch(2 位)等信息; - 两者物理位置完全重叠,无法同时存在——一旦 hashcode 写入,该字段就被占用,再也没法写入线程 ID。 这就好比一个只能单选的投票箱,你投了 hashcode 的票,线程 ID 就再也挤不进去了。 ### identity hashcode 是“一次性落盘”的不可逆操作 只要触发过 `System.identityHashCode(obj)` 或未重写的 `obj.hashCode()`,JVM 就会生成一个随机数作为 identity hashcode,并**立即写入 mark word**,且这个值永久固化。换句话说: - 哪怕只调用一次,hashcode 字段就从 0 变为非零值; - 后续所有对该对象的 `hashCode()` 调用,都会直接返回这个已经缓存好的值; - 这个写入不可撤销、不可清空,对象从此失去了“未写 hash”的初始状态。 可以理解为给对象盖了个“数字印章”,一旦盖上去,就再也擦不掉了。 ### 偏向锁机制主动拒绝已被“污染”的对象 JVM 在尝试获取偏向锁之前,会先检查 mark word 是否已经包含了 hashcode。HotSpot 源码中有一个明确的判断函数 `mark_word->has_hash_code()`: - 如果返回 true,就跳过偏向逻辑,直接走轻量级锁路径; - 这并非 bug 或配置问题,而是设计使然——为了避免出现“两次 `hashCode()` 返回不同值”这种语义错误。 所以,偏向锁不是不想接纳这个对象,而是底层硬性条件不允许。 ### 哪些操作会悄悄触发 identity hashcode 很多日常代码看似与锁无关,实则暗中“污染”了对象。你身边那些看似无害的操作,恰恰是罪魁祸首: - **日志打印**:`log.info("obj={}", obj)` → 触发 `toString()` → 默认实现里包含了 `hashCode()`; - **集合操作**:`HashSet.add(obj)`、`HashMap.containsKey(obj)` 都会隐式调用; - **字符串拼接**:`"obj=" + obj` → 同样隐式调用 `toString()`; - **断言与调试**:`Objects.requireNonNull(obj, msg)` 也会触发; - **JDK 内部**:序列化、反射参数检查、某些容器扩容逻辑,都可能暗中调用。 如果你在项目中大量使用了偏向锁优化,但又频繁进行上述操作,那偏向锁的收益很可能就大打折扣。值得多留个心眼。
本文转载于:https://www.php.cn/faq/2421177.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注