为什么 Object.hashCode() return 在运行中具有相同的值
Why does Object.hashCode() return the same value across runs
HotSpot hashCode()
上的默认实现 returns a random value and stores it in the object header. This doesn't seem to have changed in Java 8 其中哈希值是通过调用 os::random()
:
计算的
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
...
我想知道为什么 hashCode()
不断地 returns 相同的值,同样在关闭我通过执行下面的简单测试尝试的 JVM 之后,重新启动我的机器然后 运行 main()
又一次。
public class SimpleTest {
public static void main(String[] args) {
Object obj = new Object();
// This calls toString() which calls hashCode() which calls os::random()
System.out.println(obj);
}
}
如果 hashCode()
实际上是 os::random()
,怎么每次输出都一样?
java -version
给出
java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)
注:
如果有人问自己什么 System.out.println(obj);
,它调用 obj.toString()
如果对象是非 null 并产生类似 java.lang.Object@659e0bfd
的东西,与 hashCode()
:@
之后的部分是对象的十六进制哈希码(与对象在内存中的位置无关,contrary to what the documentation suggests, which has led to misunderstandings)。
确定性行为使代码更易于调试,因为它可以复制。因此,实现往往会尽可能选择它。想象一下,如果哈希每次都不同,那么复制一些由于哈希冲突处理不当(例如,在哈希长度减少后)而失败的单元测试会有多困难。
要回答你的问题,我们首先要问第二个问题,"Why is os::random()
seeded with a fixed seed?"
正如@DavidSchwartz 所建议的那样,拥有一个带有固定种子的 "random" 数字生成器非常有用,因为它可以为您提供任意但确定的行为。 JVM 开发人员可以调用 os::random()
并且仍然知道 JVM 的行为不依赖于任何外部因素。在其他好处中,这意味着 JVM 测试是可重复的;使用 "properly" 种子 RNG 将很难重现与 RNG 相关的故障。
现在我们可以回答原来的问题了,改写为"Why does HotSpot's implementation of Object.hashCode()
use os::random()
?"
这个问题的答案很可能只是因为它简单而且有效。哈希码需要均匀分布,这是 RNG 提供的。 JVM 这个领域中最简单、最容易访问的 RNG 是 os::random()
。由于 Object.hashCode()
不保证这些值的来源,因此 os::random()
根本不是随机的并不重要。
您会注意到这只是一种可能的散列策略,还定义了其他几个(并由 hashCode
global), including one which they will "likely make ... the default in future releases.
选择
最终,这只是一个实现细节。根本不需要更积极地随机化 Object.hashCode()
,其他 JVM 完全有可能不这样做,或者其他操作系统的行为不同。事实上,在 Eclipse 中,当 运行 你的代码重复时,我看到了不同的哈希码。此外,Object.hashCode()
的约定表明典型的 JVM 实现根本不会以这种方式实现 Object.hashCode()
:
This is typically implemented by converting the internal address of the object into an integer
另请注意,您的测试仅验证 对 .hashCode()
的第一个 调用是否一致。在任何类型的多线程程序中,您都无法预料到这种行为。它还不依赖于 JVM 在执行期间调用 os::random()
中的任何其他内容,它可以在任何时候执行(例如,如果垃圾收集器依赖于 os::random()
之后 .hashCode()
调用的结果第一次 GC 将是不确定的。
在执行之间使用不同的值没有任何优势。事实上,它增加了难以重现错误的机会。
重要的是,在单次执行期间,两个对象被分配相同哈希码的概率很低。
如果有人发现一个种子产生 运行 的值,需要很长时间才能产生重复(或很长的 运行,重复很少),那么它是有意义的每次都从那个开始。
我还没有检查 Hotspot 的来源以确定是否有一些努力来挑选 'good' 种子。但关键是这里的目标是有一个良好的传播。不需要随机性,从执行到执行的变化充其量是无用的,最坏的情况是无益。
HotSpot hashCode()
上的默认实现 returns a random value and stores it in the object header. This doesn't seem to have changed in Java 8 其中哈希值是通过调用 os::random()
:
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
...
我想知道为什么 hashCode()
不断地 returns 相同的值,同样在关闭我通过执行下面的简单测试尝试的 JVM 之后,重新启动我的机器然后 运行 main()
又一次。
public class SimpleTest {
public static void main(String[] args) {
Object obj = new Object();
// This calls toString() which calls hashCode() which calls os::random()
System.out.println(obj);
}
}
如果 hashCode()
实际上是 os::random()
,怎么每次输出都一样?
java -version
给出
java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)
注:
如果有人问自己什么 System.out.println(obj);
,它调用 obj.toString()
如果对象是非 null 并产生类似 java.lang.Object@659e0bfd
的东西,与 hashCode()
:@
之后的部分是对象的十六进制哈希码(与对象在内存中的位置无关,contrary to what the documentation suggests, which has led to misunderstandings)。
确定性行为使代码更易于调试,因为它可以复制。因此,实现往往会尽可能选择它。想象一下,如果哈希每次都不同,那么复制一些由于哈希冲突处理不当(例如,在哈希长度减少后)而失败的单元测试会有多困难。
要回答你的问题,我们首先要问第二个问题,"Why is os::random()
seeded with a fixed seed?"
正如@DavidSchwartz 所建议的那样,拥有一个带有固定种子的 "random" 数字生成器非常有用,因为它可以为您提供任意但确定的行为。 JVM 开发人员可以调用 os::random()
并且仍然知道 JVM 的行为不依赖于任何外部因素。在其他好处中,这意味着 JVM 测试是可重复的;使用 "properly" 种子 RNG 将很难重现与 RNG 相关的故障。
现在我们可以回答原来的问题了,改写为"Why does HotSpot's implementation of Object.hashCode()
use os::random()
?"
这个问题的答案很可能只是因为它简单而且有效。哈希码需要均匀分布,这是 RNG 提供的。 JVM 这个领域中最简单、最容易访问的 RNG 是 os::random()
。由于 Object.hashCode()
不保证这些值的来源,因此 os::random()
根本不是随机的并不重要。
您会注意到这只是一种可能的散列策略,还定义了其他几个(并由 hashCode
global), including one which they will "likely make ... the default in future releases.
最终,这只是一个实现细节。根本不需要更积极地随机化 Object.hashCode()
,其他 JVM 完全有可能不这样做,或者其他操作系统的行为不同。事实上,在 Eclipse 中,当 运行 你的代码重复时,我看到了不同的哈希码。此外,Object.hashCode()
的约定表明典型的 JVM 实现根本不会以这种方式实现 Object.hashCode()
:
This is typically implemented by converting the internal address of the object into an integer
另请注意,您的测试仅验证 对 .hashCode()
的第一个 调用是否一致。在任何类型的多线程程序中,您都无法预料到这种行为。它还不依赖于 JVM 在执行期间调用 os::random()
中的任何其他内容,它可以在任何时候执行(例如,如果垃圾收集器依赖于 os::random()
之后 .hashCode()
调用的结果第一次 GC 将是不确定的。
在执行之间使用不同的值没有任何优势。事实上,它增加了难以重现错误的机会。
重要的是,在单次执行期间,两个对象被分配相同哈希码的概率很低。
如果有人发现一个种子产生 运行 的值,需要很长时间才能产生重复(或很长的 运行,重复很少),那么它是有意义的每次都从那个开始。
我还没有检查 Hotspot 的来源以确定是否有一些努力来挑选 'good' 种子。但关键是这里的目标是有一个良好的传播。不需要随机性,从执行到执行的变化充其量是无用的,最坏的情况是无益。