Object.toString() 如何得到 "memory address" 以及我如何模仿它
How does Object.toString() get the "memory address" and how can I mimic it
Object
的toString
方法是独一无二的,它似乎是Java中唯一可以查看内存地址的地方。 Object
是怎么做到的?
我想知道,以便我可以在我自己的 class 中模仿它的实现。我不能使用 super.toString()
,因为我正在扩展 class,它已经覆盖了 toString
。
更新:我的问题的前提是问内存地址,但是答案已经表明这个前提是不正确的,所以我实际上要问的是:如何Object.toString()
return 它有什么作用,我该如何模仿它?
没有。它得到的是哈希码,不是内存地址,哈希码和内存地址没有必然联系。 (它可能在 一些 实现中。)
在基本实现中,它是 System.identityHashCode
,尽管它仍然与内存地址无关。
不是内存地址,是hashCode()
。另请参阅 Object.toString()
其中(部分)
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())
而Object.hashCode()
表示(这通常是通过将对象的内部地址转换为整数来实现的,但是Java™编程不需要这种实现技术语言。) 因此 不需要 作为内存地址,如果使用它,它仅对 Object
的内部(本机)实现可见。
如果你能在Object
Class中找到这个toString()
的定义。你会发现这个。
getClass().getName() + '@' + Integer.toHexString(hashCode()) //returns classname@hashCode.
得到的是hash-code
,不是内存地址(As per Louis Wasserman),哈希码与内存地址没有必然联系。
您应该根据需要为您在项目中声明的每个 class 覆盖此 toString()
。
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?
它没有获取地址,在 HotSpot JVM 中,它获取存储在 object 的 header 中的随机生成的 31 位哈希码。这必须存储,因为;
- 即使 object 移动并拥有新地址,哈希码也不会更改。
- 地址不够随机。地址的低 8 位始终为 0。每次 GC 后,第一个要创建的 object 始终相同。
- 地址可以是 64 位。
请在家试试这个,不适合工作!!
您可以 get/set 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
如果您知道内存是如何翻译的,您可以从参考值中获取地址。在最简单的情况下,(您有 64 位引用)引用是未翻译的,地址是存储在引用中的值。
如果您 运行 在具有 -XX:-UseCompressedOops
的 64 位 JVM 上执行此操作
// 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
这不是获取内存地址的漏洞,因为你得到的只是 object 的 header 中随机生成的 31 位 hash-code。它不提供任何获取真实内存地址的真实方法。
Object
的toString
方法是独一无二的,它似乎是Java中唯一可以查看内存地址的地方。 Object
是怎么做到的?
我想知道,以便我可以在我自己的 class 中模仿它的实现。我不能使用 super.toString()
,因为我正在扩展 class,它已经覆盖了 toString
。
更新:我的问题的前提是问内存地址,但是答案已经表明这个前提是不正确的,所以我实际上要问的是:如何Object.toString()
return 它有什么作用,我该如何模仿它?
没有。它得到的是哈希码,不是内存地址,哈希码和内存地址没有必然联系。 (它可能在 一些 实现中。)
在基本实现中,它是 System.identityHashCode
,尽管它仍然与内存地址无关。
不是内存地址,是hashCode()
。另请参阅 Object.toString()
其中(部分)
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())
而Object.hashCode()
表示(这通常是通过将对象的内部地址转换为整数来实现的,但是Java™编程不需要这种实现技术语言。) 因此 不需要 作为内存地址,如果使用它,它仅对 Object
的内部(本机)实现可见。
如果你能在Object
Class中找到这个toString()
的定义。你会发现这个。
getClass().getName() + '@' + Integer.toHexString(hashCode()) //returns classname@hashCode.
得到的是hash-code
,不是内存地址(As per Louis Wasserman),哈希码与内存地址没有必然联系。
您应该根据需要为您在项目中声明的每个 class 覆盖此 toString()
。
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?
它没有获取地址,在 HotSpot JVM 中,它获取存储在 object 的 header 中的随机生成的 31 位哈希码。这必须存储,因为;
- 即使 object 移动并拥有新地址,哈希码也不会更改。
- 地址不够随机。地址的低 8 位始终为 0。每次 GC 后,第一个要创建的 object 始终相同。
- 地址可以是 64 位。
请在家试试这个,不适合工作!!
您可以 get/set 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
如果您知道内存是如何翻译的,您可以从参考值中获取地址。在最简单的情况下,(您有 64 位引用)引用是未翻译的,地址是存储在引用中的值。
如果您 运行 在具有 -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
这不是获取内存地址的漏洞,因为你得到的只是 object 的 header 中随机生成的 31 位 hash-code。它不提供任何获取真实内存地址的真实方法。