HotSpot上的hashCode()
的默认实现返回random value并将其存储在对象标头中。在通过调用os::random()
来计算哈希值的Java 8中,这似乎没有改变:
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()
不断返回相同的值,也就是在关闭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()
的内容,那么会调用java.lang.Object@659e0bfd
,这与hashCode()
有关。 :@
之后的部分是十六进制的对象哈希码(并且与对象在内存中的位置无关,contrary与文档建议的内容无关,这导致misunderstandings。 /子>
答案 0 :(得分:6)
确定性行为使代码更容易调试,因为它可以被复制。所以实现倾向于在可能的情况下选择。想象一下,如果哈希值每次都不同,那么复制一些由于错误处理哈希冲突而导致失败的单元测试(例如,在哈希长度减少之后)是多么困难。
答案 1 :(得分:5)
要回答您的问题,我们首先要提出第二个问题,“为什么os::random()
种植了固定seed?”
正如@DavidSchwartz建议的那样,使用带有固定种子的“随机”数字生成器非常有用,因为它可以为您提供任意但确定的行为。 JVM开发人员可以调用os::random()
,但仍然知道JVM的行为不依赖于任何外部因素。除了其他好处之外,这意味着JVM测试是可重复的;使用“正确”种子RNG将难以重现与RNG相关的故障。
现在我们可以回答原来的问题,改写为“为什么HotSpot的Object.hashCode()
实施使用os::random()
?”
这个问题的答案可能只是因为它很简单,而且很有效。哈希码需要分布均匀,这是RNG提供的。 JVM的这个区域中最简单,最容易访问的RNG是os::random()
。由于Object.hashCode()
无法保证这些值的来源,因此os::random()
根本不是随机的并不重要。
您会注意到这只是一种可能的散列策略,其他几种定义(由hashCode
全局选择),包括他们将“likely make ... the default in future releases.”
最终,这只是一个实现细节。根本不需要更积极地随机化Object.hashCode()
,并且完全有可能其他JVM不这样做,或者其他操作系统表现不同。实际上,在Eclipse中,我反复运行代码时会看到不同的哈希码。此外,Object.hashCode()
的合同表明典型的JVM实现根本不以这种方式实现Object.hashCode()
:
这通常通过将对象的内部地址转换为整数
来实现
另请注意,您的测试仅验证对.hashCode()
的第一次调用是否一致。在任何类型的多线程程序中,您都不能指望这种行为。它还在执行期间JVM调用os::random()
中没有任何其他内容,它可以随时执行(例如,如果垃圾收集器依赖于os::random()
.hashCode()
调用后的结果第一个GC将是非确定性的。)
答案 2 :(得分:1)
在执行之间使用不同的值没有任何好处。实际上,它增加了难以重现错误的机会。
重要的是,在一次执行期间,为两个对象分配相同哈希码的概率很低。
如果有人发现一个种子导致一系列值需要花费很长时间才能产生重复(或者长时间运行且重复次数很少),那么每次开始都是有意义的。
我还没有查看Hotspot的来源,以确定是否有一些努力来选择一个好的&#39;种子。但关键是这里的目标是有一个良好的传播。不需要随机性,从执行到执行的变化最无用,最糟糕的是没有用。