java无法打印真实内存地址,只能通过system.identityhashcode()获取稳定对象标识,tostring()中的“@”后数值是hashcode()而非identityhashcode(),unsafe和jvm工具仅限调试且不推荐生产使用。

Java 中打印引用类型的“内存地址”其实是个常见误解——JVM 并不直接暴露真实内存地址,而是通过 hashCode() 的默认实现(通常与对象的内存地址相关,但不等价)来提供一个整数标识。严格来说,你无法打印真正的内存地址(如 0x7f8b4c1a2000),但可以获取和输出对象的哈希码(常被误称为“地址”),或借助 JVM 工具查看更底层的信息。
用 System.identityHashCode() 获取稳定哈希码
这是最常用、最安全的方式。它返回对象在 JVM 中的唯一标识整数,即使对象重写了 hashCode() 方法,该方法仍基于对象身份(类似原始哈希逻辑),且在整个生命周期内保持不变:
String s = new String("hello");
System.out.println(System.identityHashCode(s)); // 输出类似:1295142623
// 或转为十六进制,更接近传统“地址”风格
System.out.printf("%08x%n", System.identityHashCode(s)); // 输出类似:4d32a7cf
重写 toString() 时避免混淆
很多类(包括 Object 默认实现)在 toString() 中拼接了类名和 hashCode() 的十六进制形式,例如 "java.lang.String@4d32a7cf"。注意:
– 这里的 @ 后面是 hashCode() 的十六进制,不是 identityHashCode();
– 如果类重写了 hashCode(),这个值就和对象身份无关了;
– 所以不要依赖 obj.toString() 来“看地址”,应显式调用 System.identityHashCode()。
使用 Unsafe(不推荐,仅限调试/学习)
通过反射获取 Unsafe 实例,可尝试获取对象的“地址偏移量”,但这属于 JVM 内部机制,不同版本行为不一,且需要特殊 JVM 参数(如 --add-opens java.base/jdk.internal.misc=ALL-UNNAMED):
- 操作复杂、不稳定,生产环境禁用;
- 返回的是相对于堆基址的偏移,不是绝对地址;
- 现代 JVM(尤其是开启压缩指针时)地址空间被重映射,数值无直接可读意义。
借助 JVM 工具观察真实内存布局(高级)
若真需分析对象在内存中的位置,可结合以下方式:
- 启动 JVM 时添加
-XX:+PrintGCDetails -Xlog:gc+heap=debug(JDK 10+)观察 GC 日志中的对象地址; - 使用
jhsdb工具连接运行中进程,执行vm.native_memory或inspect命令; - 配合 JOL(Java Object Layout)库打印对象内存占用和字段偏移:
System.out.println(ClassLayout.parseInstance(obj).toPrintable());











