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

关键结论 ✅

  • @ 后面的 60addb54identity 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:

https://github.com/techblog-examples/jvm-memory-layout


原始标题:Memory Address of Objects in Java