web-dev-qa-db-fra.com

Existe-t-il un moyen d'obtenir une adresse de référence?

En Java, existe-t-il un moyen d'obtenir l'adresse de référence, par exemple

String s = "hello"

puis-je obtenir l'adresse de s lui-même, puis-je obtenir l'adresse de l'objet auquel la référence se réfère?

31
Adam Lee

Vous pouvez obtenir l'index d'objet avec Unsafe. Selon la façon dont la JVM utilise la mémoire (adresses 32 bits, index 32 bits, index 32 bits avec décalage, adresse 64 bits) peut affecter l'utilité de l'index d'objet.

Voici un programme qui suppose que vous avez un index 32 bits dans une machine virtuelle Java 64 bits.

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

Fonctionnant sur Java 6 mise à jour 26 (64 bits avec oops compressés) et Java 7. Remarque: les adresses et les adresses relatives sont en hexadécimal).

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

OU parfois

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

Non vous ne pouvez pas. Même en utilisant l'interface Java Native Interface (JNI), vous ne pouvez obtenir qu'un descripteur opaque de la structure de données, pas un pointeur vers le véritable objet JVM.

Pourquoi voudriez-vous une telle chose? Ce ne serait pas nécessairement sous une forme que vous pourriez utiliser pour quoi que ce soit, de toute façon, même à partir de code natif.

5

Oui, vous pouvez le faire avec dangereux, bien que ce ne soit pas si directement. Mettez l'objet ou la référence d'instance dans un int [], c'est ok. long [] devrait également convenir.

    @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

En fait, l'adresse peut être obtenue avec Sun.misc.Unsafe mais c'est vraiment très dangereux. GC déplace souvent des objets.

3
Vadzim

Il n'est pas possible dans Java d'obtenir une adresse de référence d'un objet, comme votre chaîne. L'adresse de référence d'un objet est cachée à l'utilisateur, en Java.

En C, vous pouvez le faire, grâce au concept de pointeurs. Java a un concept similaire, à bas niveau, et ceci est l'adresse de référence. La référence est comme un pointeur C, mais elle n'est pas explicite. En C, vous pouvez faire l'opération de référencement de pointeurs, via le *, mais en Java, ce n'est pas possible.

Je n'aime pas beaucoup le langage C, aussi parce que les pointeurs, selon moi, ne sont pas un concept facile à gérer. C'est l'une des raisons pour lesquelles j'aime Java, car le programmeur n'a pas besoin de se soucier du pointeur d'un objet.

Comme le dit @jarnbjo, vous pouvez vérifier, si certaines références sont similaires, avec une syntaxe comme celle-ci:

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

Notez que == Vérifie l'égalité de l'adresse de référence. Si vous souhaitez vérifier l'égalité de la valeur des chaînes, utilisez la méthode equals().

Je vous suggère de lire this SO question, this page Wikipedia et this page.

1
Alberto Solano