Как возражает.toString () получает "адрес памяти" и как я могу его имитировать

на toString метод Object уникален тем, что он кажется единственным местом в Java, где адрес памяти доступен для просмотра. Как это Object этого?

Я хотел бы знать, чтобы я мог имитировать его реализацию в своем собственном классе. Я не могу использовать super.toString() потому что я расширяю класс, который переопределяет toString уже.

Update: предпосылка моего вопроса запрашивает адрес памяти, но ответы указали, что эта предпосылка неверна, поэтому на самом деле я спрашиваю: как Object.toString() Возвращение, что он делает, и как я могу имитировать это?

5 ответов


это не адрес памяти, это hashCode(). См. также Object.toString() который говорит (частично)

The toString метод класса Object возвращает строку, состоящую из имени класса, экземпляром которого является объект, символа at-sign @, и беззнаковое шестнадцатеричное представление хэш-кода объекта. Другими словами, этот метод возвращает строку, равную стоимости:

getClass().getName() + '@' + Integer.toHexString(hashCode())

и Object.hashCode() говорит (это обычно реализуется путем преобразования внутренний адрес объекта в целое число, но этот метод реализации не требуется язык программирования Java™.) так это не требуются быть адресом памяти, и если он используется, он виден только для внутренней (родной) реализации Object.


метод toString объекта уникален тем, что он кажется единственным местом в Java, где адрес памяти доступен для просмотра. Как объект делает это?

он не получает адрес, в HotSpot JVM он получает случайно сгенерированный 31-битный хэш-код, хранящийся в заголовке объекта. Это должно быть сохранено, потому что;

  • хэш-код не может измениться, даже если объект перемещен и имеет новый адрес.
  • адрес не достаточно случайно. Младшие 8 бит адреса всегда равен 0. После каждого GC первый объект, который будет создан, всегда один и тот же.
  • адрес может быть 64-разрядной.

ПОПРОБУЙТЕ ЭТО ДОМА, НЕ ПОДХОДИТ ДЛЯ РАБОТЫ!!.

вы можете получить/установить hashCode () с помощью Unsafe

static final Unsafe UNSAFE;

static {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        UNSAFE = (Unsafe) theUnsafe.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

public static void setIdentityHashCode(Object o, int code) {
    UNSAFE.putInt(o, 1l, code & 0x7FFF_FFF);
}

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Double d = 1.0;
    Double d2 = 1.0;
    setIdentityHashCode(d, 1);
    setIdentityHashCode(d2, 1);
    System.out.println("d: "+d+" System.identityHashCode(d): "+System.identityHashCode(d));
    System.out.println("d2: "+d2+" System.identityHashCode(d2): "+System.identityHashCode(d2));
    System.out.println("d == d2: " + (d == d2));
}

печать

d: 1.0 System.identityHashCode(d): 1
d2: 1.0 System.identityHashCode(d2): 1
d == d2: false

вы можете получить адрес из опорного значения при условии, что вы знаете, как была переведена память. В простейшем case, (где у вас есть 64-разрядные ссылки) ССЫЛКА непереведена, а адрес-это значение, хранящееся в ссылке.

если вы запустите это на 64-разрядной JVM с -XX:-UseCompressedOops

// This only works if a GC doesn't move the object while attempting to access it.
static final Unsafe UNSAFE;

static {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        UNSAFE = (Unsafe) theUnsafe.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

// run with: -ea -XX:-UseCompressedOops
public static void main(String[] args) {
    Object i = 0x12345678;
    System.out.printf("indentityHashCode = %08x%n", System.identityHashCode(i));

    Object[] obj = { i };
    assert Unsafe.ARRAY_OBJECT_INDEX_SCALE == 8; // 8 bytes per reference.
    long address = UNSAFE.getLong(obj, (long) Unsafe.ARRAY_OBJECT_BASE_OFFSET);
    System.out.printf("%x%n", address);
    for (int j=0;j<24;j++)
        System.out.printf("%02x ", UNSAFE.getByte(address + j) & 0xFF);
    System.out.println();
    // now some really scary sh!t
    UNSAFE.putLong(i, 8L, UNSAFE.getLong(0L, 8L));
    System.out.printf("`i` is now a %s and is %x%n", i.getClass(), i);
}

печать

indentityHashCode = 5a07e868
7fbf41cb8560
01 68 e8 07 5a 00 00 00 48 33 3f b9 b9 7f 00 00 78 56 34 12 00 00 00 00 
   ^^hashCode^          ^class address  ^       ^int value^
`i` is now a class java.lang.Long and is 12345678

это не так. Он получает хэш-код, а не адрес памяти, и хэш-код не имеет необходимого соединения с адресом памяти. (Это может быть в некоторые реализаций.)

в базовой реализации, то есть System.identityHashCode, хотя он по-прежнему не имеет никакого отношения к адресу памяти.


если вы можете найти определение этого toString() на Object класса. Вы найдете это.

getClass().getName() + '@' + Integer.toHexString(hashCode()) //returns classname@hashCode.

что это hash-code, не адрес памяти (согласно Луису Вассерману) , а хэш-код не имеет необходимого соединения с адресом памяти.

вы должны переопределить этот toString() для каждого класса, который вы объявили в своем проекте в соответствии с вашими потребностями.


Это не лазейка для получения адреса памяти, потому что все, что вы получаете, это случайно сгенерированный 31-битный хэш-код в заголовке объекта. Он не предоставляет никакого реального способа получить реальный адрес памяти.