深入分析Java中打印对象内存地址 System.identityHashCode()方法
地址是什么样子的
我们知道system.out.println(new XXX())可以打印出内存地址 我们看个例子:
public class Test { public static void main(String[] args) { Test t = new Test(); System.out.println(t);
看下输出的结果:
Test@139a55
toString()方法
这个结果是怎么来的呢,其实是调用了Test的toString()方法,该方法继承自Object
public class Object { public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public native int hashCode();
看到这里,toString方法其实是打印了对象自身的hashCode,Object的hashCode() 是一个native方法,默认返回的是内存地址。toString方法打印不是hashCode()返回的原值,而是经过转化的16进制字符。
重写hashCode
我们知道,hashCode()可以重写,如果重写的话,同一个类型的2个实例,hashCode的值是相同的,但是地址肯定是不相同的,此时,如果想打印出地址怎么办?这时,不能直接调用hashCode了 我们再看个例子:
public class TestMem { public static void main(String[] args) { String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; String s4 = s1+s2; System.out.println(s3.equals(s4)); System.out.println(s3==s4); System.out.println(s3.hashCode()); System.out.println(s4.hashCode()); System.out.println(System.identityHashCode(s3)); System.out.println(System.identityHashCode(s4)); } }
结果:
true //字面值相同 false //地址不同 -1524582912 //对比2个发现hashcode相同 -1524582912 1284693 31168322
通过结果,我们发现s3和s4的字面值是相同的,但地址是不相同的,字面值相同是因为String类虽然继承了Object,但是重写了hashcode,地址不同可以通过identityHashCode打印出来
identityHashCode与真正的内存地址
严格来说,identityHashCode()并不指向真实的地址,关于对象的真正地址,可以参见
要在 JVM 中查找对象的内存地址,需要 Java 对象布局 ( JOL ) 工具。
首先,添加 jol-core依赖:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency>
要在 JVM 中查找特定对象的内存地址,我们可以使用 addressOf() 方法:
String answer = "42"; System.out.println("The memory address is " + VM.current().addressOf(answer));
这将打印:
The memory address is 31864981224
HotSpot JVM 中有不同的压缩引用模式。由于这些模式,此值可能不完全准确。因此,我们不应该根据该地址执行一些本机内存操作,因为它可能会导致奇怪的内存损坏。
此外,大多数 JVM 实现中的内存地址会随着 GC 不时移动对象而发生变化。