上周有同学问,对象的hashcode是否就是它的地址?如何获取Java中一个对象的地址?
首先我们来看第一个问题——对象的hashcode是否是它的地址,这个问题的答案是这个取决于具体的实现,一般来说,它常常是对象的初始地址的整数表示。我们可以从JDK中Object类中hashcode()方法的注释看出来:
其中,重点是这句
* This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.
翻译成中文是,这个方法的典型实现是通过把对象的内部地址转为为整数,但是这种实现技术并不是Java编程语言所规定的。
为什么我还在最上面的回答加上一个初始地址呢?因为Java中的对象是JVM在管理,JVM会在她认为合适的时候对对象进行移动,比如,在某些需要整理内存碎片的GC算法下发生的GC。既然,对象的地址会变动,那么第二个问题还有意义么?对不起,确实没意义。但是我们仍然有权利知道我们想知道的一切,下面的代码展示了如何获取对象的地址,同时也对GC前后对象的地址进行了输出,便于比较。
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
public class OrderOfObjectsAfterGCMain {
static final Unsafe unsafe = getUnsafe();
static final boolean is64bit = true;
public static void main(String... args) {
Double[] ascending = new Double[16];
for(int i=0;i<ascending.length;i++)
ascending[i] = (double) i;
System.out.println("Before GC");
printAddresses("ascending", ascending);
System.gc();
System.out.println("\nAfter GC");
printAddresses("ascending", ascending);
}
public static void printAddresses(String label, Object... objects) {
System.out.print(label + ": 0x");
long last = 0;
int offset = unsafe.arrayBaseOffset(objects.getClass());
int scale = unsafe.arrayIndexScale(objects.getClass());
switch (scale) {
case 4:
long factor = is64bit ? 8 : 1;
final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;
System.out.print(Long.toHexString(i1));
last = i1;
for (int i = 1; i < objects.length; i++) {
final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;
if (i2 > last)
System.out.print(", +" + Long.toHexString(i2 - last));
else
System.out.print(", -" + Long.toHexString( last - i2));
last = i2;
}
break;
case 8:
throw new AssertionError("Not supported");
}
System.out.println();
}
private static Unsafe getUnsafe() {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
结果是:
好的,我们顺利得到了对象的地址,同时也对GC前后的地址进行了比较,发现他们确实发生了变化。最后,提醒下大家:
Unsafe 这个类是SUN JDK的内部实现细节,我们不应该过多关注她,同时,上面提供获取对象地址的方法也不具有通用性,不仅指J9、JRockit这样的不同的JVM,甚至JVM的不同版本也有可能没法用,所以,还是消灭这个“伪需求”,好好睡觉吧!
created by 刘Sir