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?
It is not the memory address, it is the hashCode()
. See also Object.toString()
which says (in part)
The
toString
method for classObject
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
.
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
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.
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.
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