1. 概述
本文将带你了解如何在 Java 中获取对象的内存地址。
需要提前说明的是,JVM 运行时数据区的内存布局 并不在 JVM 规范中强制定义,而是由具体实现自行决定(参考 JVM 规范文档)。这意味着不同的 JVM 实现(如 HotSpot、OpenJ9)在对象和数组的内存排布策略上可能完全不同,进而影响内存地址的表现形式。
本文聚焦于 HotSpot JVM 的实现细节,后续提到的 JVM 如无特别说明,均指 HotSpot。
⚠️ 注意:获取内存地址主要用于调试、性能分析或底层原理研究,生产环境切勿依赖此信息做逻辑判断。
2. 所需依赖
要查看 JVM 中对象的内存布局和地址,我们将使用 Java Object Layout(JOL) 工具。它是由 OpenJDK 提供的权威工具,能精确展示对象在内存中的真实结构。
引入 Maven 依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
✅ 推荐使用 0.10 或更高版本,兼容 Java 8+。
3. 获取对象内存地址
JOL 提供了 VM.current().addressOf()
方法,可直接获取对象在堆中的内存地址(逻辑地址):
String answer = "42";
System.out.println("The memory address is " + VM.current().addressOf(answer));
输出示例:
The memory address is 31864981224
踩坑提醒 ⚠️
- 地址非真实物理地址:HotSpot 使用了 Compressed OOPs(压缩指针) 技术,这意味着你看到的地址是经过压缩编码的逻辑地址,并非真实的物理内存位置。
- GC 会移动对象:Java 的垃圾回收器(尤其是 G1、ZGC 等)会在运行时移动对象以整理堆空间,因此同一个对象在不同时刻的地址可能不同。
- 禁止用于指针操作:你不能像 C/C++ 那样通过这个地址直接操作内存,否则会导致 JVM 崩溃或不可预知的行为。
❌ 错误用法示例(切勿模仿):
// 想通过地址做“指针偏移”?别想了,Java 不支持!
long addr = VM.current().addressOf(obj);
// ... 试图解析字段偏移?用 Unsafe 或 JOL 提供的工具才是正道
4. 关于 Identity Hash Code 的误解澄清
一个非常常见的误区是:认为 Object.toString()
输出中的十六进制部分(如 java.lang.Object@60addb54
)就是对象的内存地址。
我们来验证一下:
Object obj = new Object();
System.out.println("Memory address: " + VM.current().addressOf(obj));
System.out.println("toString: " + obj);
System.out.println("hashCode: " + obj.hashCode());
System.out.println("identityHashCode: " + System.identityHashCode(obj));
输出结果:
Memory address: 31879960584
toString: java.lang.Object@60addb54
hashCode: 1622006612
identityHashCode: 1622006612
关键结论 ✅
@
后面的60addb54
是 identity hash code 的十六进制表示,即Integer.toHexString(System.identityHashCode(obj))
。hashCode()
在未重写时,默认返回的就是 identity hash code,由 JVM 在对象首次调用hashCode()
时生成。- memory address ≠ identity hash code,两者完全不是一回事。
💡 小知识:identity hash code 生成策略因 JVM 实现而异,可能是基于内存地址的哈希,也可能是随机数或递增 ID。但从 Java 17 开始,HotSpot 已不再直接使用地址计算 hash code。
5. 总结
本文通过 JOL 工具展示了如何获取 Java 对象的内存地址,并澄清了关于 toString()
和 hash code 的常见误解。
核心要点回顾:
- ✅ 使用 JOL 的
VM.current().addressOf()
可查看对象逻辑地址 - ⚠️ 地址受压缩指针和 GC 移动影响,不具备稳定性
- ❌
Object@xxxx
中的xxxx
是 identity hash code 的十六进制,不是内存地址 - 🔍 identity hash code 与内存地址无直接绑定关系,不应混为一谈
所有示例代码已托管至 GitHub: