为什么Object.hashCode()在运行中返回相同的值

Mat*_*aun 8 java random hashcode

hashCode()HotSpot 的默认实现返回一个随机值并将其存储在对象头中.这在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
...
Run Code Online (Sandbox Code Playgroud)

我想知道为什么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);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果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)
Run Code Online (Sandbox Code Playgroud)

注意:

如果有人问自己是什么System.out.println(obj);,它调用 obj.toString()该对象是否非空,产生类似java.lang.Object@659e0bfd,有做hashCode():后一部分@是对象的散列码十六进制(和是无关的,在内存中的对象的位置,相反给什么文件表明,这导致了误解).

Dav*_*rtz 6

确定性行为使代码更容易调试,因为它可以被复制.所以实现倾向于在可能的情况下选择.想象一下,如果哈希每次都不同,那么复制一些由于错误处理哈希冲突而失败的单元测试(例如,在哈希长度缩短之后)是多么困难.

  • 这似乎不是一个很好的理由; 如果您正在编写依赖于`.hashCode()`行为的测试,那么您应该测试已明确定义`.hashCode()`方法的对象.依靠`Object.hashCode()`保持稳定只会引发问题,特别是升级JVM版本时.如果有的话,这是一个在运行之间显式地使`Object.hashCode()`volatile的参数. (2认同)

dim*_*414 5

要回答你的问题,我们首先要问第二个问题,"为什么os::random()种子会固定种子?"

正如@DavidSchwartz所说,拥有一个带有固定种子的"随机"数字生成器非常有用,因为它可以为您提供任意但确定的行为.JVM开发人员可以调用os::random()并且仍然知道JVM的行为不依赖于任何外部因素.除了其他好处之外,这意味着JVM测试是可重复的; 使用"适当"种子RNG将难以重现与RNG相关的故障.

现在我们可以回答原来的问题,改写为"为什么HotSpot的实现Object.hashCode()使用os::random()?"

这个问题的答案很可能只是因为它很简单,而且很有效.哈希码需要分布均匀,这是RNG提供的.JVM的这个区域中最简单,最容易访问的RNG是os::random().由于Object.hashCode()不能保证这些值的来源,因此根本不是os::random()随机的并不重要.

您会注意到这只是一种可能的散列策略,其他几种定义(并由hashCode全局选择),包括它们" 可能会成为......未来版本中的默认值 ".

最终,这只是一个实现细节.没有必要更积极地随机化Object.hashCode(),并且完全有可能其他JVM不这样做,或者其他操作系统表现不同.实际上,在Eclipse中,我反复运行代码时会看到不同的哈希码.此外,合同Object.hashCode()建议典型的JVM实现根本不实现Object.hashCode()这种方式:

这通常通过将对象的内部地址转换为整数来实现


另请注意,您的测试仅验证第一次调用.hashCode()是否一致.在任何类型的多线程程序中,您都不能指望这种行为.它还可以os::random()在执行期间依赖于JVM调用中的任何其他内容,它可以随时执行(例如,如果垃圾收集器在第一个GC将是非确定性之后依赖于调用os::random()的结果.hashCode()).