System.identityHashCode()返回JVM分配的唯一稳定标识而非真实内存地址,因JVM为安全、GC移动性及跨平台性不暴露原始地址;它与equals/hashCode无关,即使对象被移动或类重写hashCode也保持不变。

Java 中 Object 类的 hashCode() 方法默认**不直接返回内存地址**,而是由 JVM 以平台相关的方式生成一个与对象身份(identity)关联的整数值,通常称为“identity hash code”。它**逻辑上等价于对象的内存地址**,但实现上并非简单取地址值(比如不能直接用 C 的 &obj),原因包括:地址可能超出 int 范围、GC 会移动对象、不同 JVM 实现有差异等。
为什么不是真实内存地址?
现代 JVM(如 HotSpot)使用以下机制避免直接暴露或依赖物理地址:
- 对象在堆中可能被垃圾回收器(如 G1、ZGC)移动,物理地址会变,但 identity hash code 必须保持不变(只要对象没被重定义
hashCode()); - 64 位系统下指针是 8 字节,而
int只有 4 字节,无法无损存储; - JVM 为每个对象在首次调用
hashCode()时,基于其初始分配位置、线程 ID、随机种子等计算并缓存一个 32 位整数,后续调用直接返回该缓存值。
HotSpot 中 identity hash code 的典型生成逻辑
以 OpenJDK HotSpot 为例(src/hotspot/share/runtime/synchronizer.cpp):
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 首次调用时,JVM 从对象头(mark word)中读取是否已存储 hash;若未设置,则进入
get_next_hash()计算; - 计算方式取决于 JVM 启动参数
-XX:HashSeed和-XX:hashCode(默认值为 5); - 常见策略包括:
• 使用对象头中存储的随机数(启动时生成)与对象地址做异或/移位混合;
• 基于当前线程状态、时间戳、全局计数器生成伪随机值;
• 对象首次分配的内存页地址 + 线程局部哈希种子,再取低 32 位。 - 结果写入对象头 mark word 的 hash 位段(如果空间足够),之后直接复用。
如何验证它和地址无关但具唯一性?
可以借助 Unsafe 或 JOL(Java Object Layout)观察,但注意:
-
System.identityHashCode(obj)返回的就是该 identity hash,即使对象重写了hashCode()也能获取原始值; - 两个不同对象即使内容相同(如
new String("a")两次),System.identityHashCode()几乎总是不同; - 同一对象在多次 GC 后仍返回相同 identity hash —— 证明它被缓存且与运行时地址解耦。
开发中要注意什么?
除非特殊用途(如实现弱引用哈希容器、调试对象生命周期),一般无需关心其具体算法:
- 不要假设 identity hash 与
System.nanoTime()或内存地址有可预测关系; - 不要用它做跨 JVM 进程的持久化标识(每次重启、不同 JVM 实现结果都可能不同);
- 若需稳定哈希,请显式重写
hashCode(),依据业务字段计算; - 注意:
String、Integer等类已重写hashCode(),返回的是内容哈希,不是 identity hash。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










