Есть ли способ получить ссылочный адрес?

В Java есть способ получить ссылочный адрес, скажем

String s = "hello"

Могу ли я получить адрес самого s, также могу ли я получить адрес объекта, на который ссылается ссылка?

Ответ 1

Вы можете получить индекс объекта с помощью Unsafe. В зависимости от того, как JVM использует память (32-разрядные адреса, 32-разрядный индекс, 32-разрядный индекс со смещением, 64-разрядный адрес) могут влиять на полезность индекса объекта.

Вот программа, предполагающая, что у вас 32-разрядный индекс в 64-разрядной JVM.

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 (64-разрядная с сжатыми oops) и Java 7. Примечание: адреса и относительные адреса указаны в шестнадцатеричном формате.

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

Ответ 2

Нет, вы не можете. Даже используя Java Native Interface (JNI), вы можете получить непрозрачный дескриптор структуры данных, а не указатель на реальный объект JVM.

Зачем вам это нужно? Это не обязательно будет в форме, которую вы могли бы использовать для чего угодно, во всяком случае, даже из собственного кода.

Ответ 3

Нет, вы не можете. Особенно, если Java может (и AFAIK) перемещать объекты в куче, если это необходимо для уплотнения памяти во время сбора мусора.

Зачем вам это нужно или нужно?

Ответ 4

Да, вы можете сделать это с помощью Unsafe, Althrough это не так прямо. Поместите ссылку на объект или экземпляр в 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;
}

Ответ 5

Фактически адрес можно получить с помощью sun.misc.Unsafe, но это действительно очень опасно. GC часто перемещает объекты.

Ответ 6

Невозможно в Java получить ссылочный адрес объекта, например, вашу String. Адрес ссылки объекта скрыт для пользователя, на Java.

В C вы можете сделать это, используя концепцию указателей. Java имеет аналогичную концепцию на низком уровне, и это ссылочный адрес. Ссылка похожа на C-указатель, но не явный. В C вы можете выполнить операцию привязки указателей, используя *, но в Java это невозможно.

Я не очень люблю язык C, также потому, что указатели, по моему мнению, нелегко понять. Это одна из причин, по которой мне нравится Java, потому что программисту не нужно беспокоиться об указателе объекта.

Как и @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().

Я предлагаю вам прочитать этот вопрос SO, this Страница Википедии и эта страница.