web-dev-qa-db-ja.com

参照アドレスを取得する方法はありますか?

Javaでは、参照アドレスを取得する方法はありますか?

String s = "hello"

s自体のアドレスを取得できますか、また、参照が参照するオブジェクトのアドレスを取得できますか?

31
Adam Lee

Unsafeを使用してオブジェクトインデックスを取得できます。 JVMがメモリを使用する方法(32ビットアドレス、32ビットインデックス、オフセット付き32ビットインデックス、64ビットアドレス)によって、オブジェクトインデックスの有用性に影響する場合があります。

64ビットJVMに32ビットインデックスがあることを前提とするプログラムを次に示します。

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; // auto detect if possible.

    public static void main(String... args) {
        Double[] ascending = new Double[16];
        for(int i=0;i<ascending.length;i++)
            ascending[i] = (double) i;

        Double[] descending = new Double[16];
        for(int i=descending.length-1; i>=0; i--)
            descending[i] = (double) i;

        Double[] shuffled = new Double[16];
        for(int i=0;i<shuffled.length;i++)
            shuffled[i] = (double) i;
        Collections.shuffle(Arrays.asList(shuffled));

        System.out.println("Before GC");
        printAddresses("ascending", ascending);
        printAddresses("descending", descending);
        printAddresses("shuffled", shuffled);

        System.gc();
        System.out.println("\nAfter GC");
        printAddresses("ascending", ascending);
        printAddresses("descending", descending);
        printAddresses("shuffled", shuffled);

        System.gc();
        System.out.println("\nAfter GC 2");
        printAddresses("ascending", ascending);
        printAddresses("descending", descending);
        printAddresses("shuffled", shuffled);
    }

    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);
        }
    }
}

Java 6 update 26(圧縮OOP付き64ビット)およびJava 7.で実行します。注:アドレスおよび相対アドレスは16進数です。

Before GC
ascending: 0x782322b20, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18
descending: 0x782322e58, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
shuffled: 0x782322ec0, +78, -30, +90, -c0, +18, +90, +a8, -30, -d8, +f0, -30, -90, +60, -48, +60

After GC
ascending: 0x686811590, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
descending: 0x686811410, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
shuffled: 0x686811290, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18

または時々

Before GC
ascending: 0x782322b20, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18, +18
descending: 0x782322e58, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18
shuffled: 0x782323028, -168, +150, -d8, -30, +60, +18, +30, +30, +18, -108, +30, -48, +78, +78, -30

After GC
ascending: 0x6868143c8, +4db0, +7120, -bd90, +bda8, -bd90, +4d40, +18, +18, -12710, +18, +80, +18, +ffa8, +220, +6b40
descending: 0x68681d968, +18, +d0, +e0, -165d0, +a8, +fea8, +c110, -5230, -d658, +6bd0, +be10, +1b8, +75e0, -19f68, +19f80
shuffled: 0x686823938, -129d8, +129f0, -17860, +4e88, +19fe8, -1ee58, +18, +18, +bb00, +6a78, -d648, -4e18, +4e40, +133e0, -c770
39
Peter Lawrey

いいえ、あなたがすることはできません。 Java Native Interface(JNI))を使用しても、データ構造への不透明なハンドルのみを取得でき、実際のJVMオブジェクトへのポインタは取得できません。

なぜそんなことをしたいのですか?とにかく、ネイティブコードからでも、何にでも使用できる形式である必要はありません。

はい、安全ではありませんが、直接ではありません。オブジェクトまたはインスタンスの参照をint []に入れます、それで構いません。 long []も問題ないはずです。

    @Test
public void test1() throws Exception {
    Unsafe unsafe = Util.unsafe;
    int base = unsafe.arrayBaseOffset(int[].class);
    int scale = unsafe.arrayIndexScale(int[].class);
    int shift = 31 - Integer.numberOfLeadingZeros(scale);
    System.out.printf("base: %s, scale %s, shift: %s\n", base, scale, shift);
    base = unsafe.arrayBaseOffset(Object[].class);
    scale = unsafe.arrayIndexScale(Object[].class);
    shift = 31 - Integer.numberOfLeadingZeros(scale);
    System.out.printf("base: %s, scale %s, shift: %s\n", base, scale, shift);
    int[] ints = { 1, 2, 0 };
    String string = "abc";
    System.out.printf("string: id: %X, hash: %s\n", System.identityHashCode(string), string.hashCode());
    unsafe.putObject(ints, offset(2, shift, base), string);
    System.out.printf("ints: %s, %X\n", Arrays.toString(ints), ints[2]);
    Object o = unsafe.getObject(ints, offset(2, shift, base));
    System.out.printf("ints: %s\n", o);
    assertSame(string, o);

    Object[] oa = { 1, 2, string };
    o = unsafe.getObject(oa, offset(2, shift, base));
    assertSame(string, o);
    int id = unsafe.getInt(oa, offset(2, shift, base));
    System.out.printf("id=%X\n", id);
}

public static long offset(int index, int shift, int base) {
    return ((long) index << shift) + base;
}
4
qinxian

実際には、アドレスは Sun.misc.Unsafe で取得できますが、実際には非常に安全ではありません。 GCはしばしばオブジェクトを移動します。

3
Vadzim

Javaでは、文字列のようなオブジェクトの参照アドレスを取得することはできません。オブジェクトの参照アドレスは、Javaではユーザーに隠されます。

Cでは、ポインターの概念を通じてこれを行うことができます。 Javaには低レベルで同様の概念があり、これは参照アドレスです。参照はCポインターに似ていますが、明示的ではありません。Cでは、参照の操作を実行できます。 _*_を介したポインターの、しかしJavaでは不可能です。

私によると、ポインタは管理が容易な概念ではないため、C言語はあまり好きではありません。これは、プログラマがオブジェクトのポインタを心配する必要がないため、Javaが好きな理由の1つです。

@jarnbjoが言うように、いくつかの参照が似ているかどうか、次のような構文で確認できます。

_String s = "hello";
String g = s;
System.out.println("Are the reference addresses similar? "+(s==g));
g = "goodbye";
System.out.println("Are the reference addresses similar? "+(s==g));
_

_==_は参照アドレスの等価性をチェックすることに注意してください。文字列の値が等しいかどうかを確認する場合は、equals()メソッドを使用します。

this SO question、 this Wikipedia page and this page。

1
Alberto Solano