System.identityHashCode 返回对象创建时确定且永不改变的身份标识整数,基于对象头中固定值生成,不受GC移动、压缩指针或hashCode重写影响,null返回0,同一对象多次调用结果恒定。

System.identityHashCode 返回的是 JVM 为对象分配的、与内容无关的身份标识整数,不是真实内存地址,也不受任何重写影响。
它基于对象头中的固定标识生成
对象在创建时,JVM 就在对象头(如 mark word)中写入一个初始身份哈希值。这个值一旦确定,整个生命周期内保持不变——即使对象被 GC 移动、压缩指针启用、或类重写了 hashCode(),它都不变。
- HotSpot 中通常由对象首次访问时的地址(或其变换)推导,但不直接暴露地址
- GC 移动后,JVM 会保留原始 identityHashCode,而非更新为新地址
- 启用 -XX:+UseCompressedOops 时,地址已压缩,更无法映射为 int 值
它完全绕过类的 hashCode() 方法逻辑
调用 obj.hashCode() 是运行时动态分派:先查类是否重写,再执行对应方法;而 System.identityHashCode() 是静态 native 方法,直接读取对象头字段,不触发任何 Java 层方法调用。
- String、ArrayList、自定义重写类 —— 全部一视同仁,返回物理身份码
- null 传入时返回 0,这是唯一约定的特殊值
- 同一对象多次调用结果恒定,可安全用于缓存键、调试追踪等场景
它不保证全局唯一,但足够稳定实用
identityHashCode 是 32 位 int,理论上存在碰撞可能;JVM 不承诺跨进程、跨重启一致,但在单次运行中对存活对象具备强稳定性。
- IdentityHashMap 就依赖它做键哈希,冲突时用 == 判断而非 equals
- 调试时区分两个 new String("abc") 实例,靠的就是它返回不同值
- 不能替代 == 判断引用相等,但它和 == 在多数情况下指向同一语义基础
它和 Object.hashCode() 的关系是“退化而非等价”
如果一个类没重写 hashCode(),Object 默认实现底层可能复用 identityHashCode;但一旦重写,两者就彻底分道扬镳——前者看内容,后者看身份。
- 比如 new Person("Alice").hashCode() 返回 name 字符串哈希
- 而 System.identityHashCode(new Person("Alice")) 每次新建实例都返回不同值
- 这种差异正是它在序列化跟踪、弱引用缓存、代理管理中不可替代的原因


















