作者:谢海武181_160 | 来源:互联网 | 2023-05-16 10:23
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
...
我想知道为什么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()
:后一部分@
是对象的散列码十六进制(和是无关的,在内存中的对象的位置,相反给什么文件表明,这导致了误解).
1> David Schwar..:
确定性行为使代码更容易调试,因为它可以被复制.所以实现倾向于在可能的情况下选择.想象一下,如果哈希每次都不同,那么复制一些由于错误处理哈希冲突而失败的单元测试(例如,在哈希长度缩短之后)是多么困难.
这似乎不是一个很好的理由; 如果您正在编写依赖于`.hashCode()`行为的测试,那么您应该测试已明确定义`.hashCode()`方法的对象.依靠`Object.hashCode()`保持稳定只会引发问题,特别是升级JVM版本时.如果有的话,这是一个在运行之间显式地使`Object.hashCode()`volatile的参数.
2> dimo414..:
要回答你的问题,我们首先要问第二个问题,"为什么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()
).