Java中如何获取对象(引用)地址?

上周有同学问,对象的hashcode是否就是它的地址?如何获取Java中一个对象的地址?
首先我们来看第一个问题——对象的hashcode是否是它的地址,这个问题的答案是这个取决于具体的实现,一般来说,它常常是对象的初始地址的整数表示。我们可以从JDK中Object类中hashcode()方法的注释看出来:
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
这里写图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值