How does Object.toString() get the “memory address” and how can I mimic it

本小妞迷上赌 提交于 2020-01-01 08:58:41

问题


The toString method of Object is unique in that it seems to be the only place in Java where the memory address is viewable. How does Object do this?

I would like to know so that I can mimic its implementation in a class of my own. I can't use super.toString() because I'm extending a class which overrides toString already.

Update: The premise of my question asks for the memory address, but the answers have indicated that this premise is incorrect, so what I am actually asking is: How does Object.toString() return what it does, and how can I mimic it?


回答1:


It is not the memory address, it is the hashCode(). See also Object.toString() which says (in part)

The toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character @, and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of:

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

And Object.hashCode() says (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.) so it's not required to be a memory address, and if used it's only visible to the internal (native) implementation of Object.




回答2:


The toString method of Object is unique in that it seems to be the only place in Java where the memory address is viewable. How does Object do this?

It doesn't get the address, in the HotSpot JVM, it gets a randomly generated 31-bit hashcode stored in the header of the object. This has to be stored because;

  • the hashcode cannot change even if the object is moved and has a new address.
  • the address is not random enough. The lower 8-bits of the address are always 0. After every GC, the first object to be creates is always the same.
  • the address could be 64-bit.

DO TRY THIS AT HOME, NOT SUITABLE FOR WORK!!.

You can get/set the hashCode() using 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));
}

prints

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

You can get the address from the reference value provided you know how the memory has been translated. In the simplest case, (where you have 64-bit references) the reference is untranslated and the address is the value stored in the reference.

If you run this on a 64-bit JVM with -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);
}

prints

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



回答3:


It doesn't. What it gets is the hash code, not the memory address, and the hash code has no necessary connection to the memory address. (It might in some implementations.)

In the base implementation, that's System.identityHashCode, though it still has no relationship to the memory address.




回答4:


If you may find the definition of this toString() in Object Class. You will find this.

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

What it gets is the hash-code , not the memory address (As per Louis Wasserman) , and the hash code has no necessary connection to the memory address.

you should override this toString() for each class that you declared in your Project as per your Need.




回答5:


This is not a loophole to get a hold of a memory address because all you get is a randomly generated 31 bit hash-code in the header of the object. It doesn't provide any real way get a hold of a real memory address.



来源:https://stackoverflow.com/questions/36376339/how-does-object-tostring-get-the-memory-address-and-how-can-i-mimic-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!