Java Object.hashCode() - 地址或随机数?

25
我正在尝试理解hashCode()方法的本地实现。这个方法到底返回什么?它是一个内存地址还是一个随机值?

4
OpenJDK源代码应该能告诉你。我怀疑它不是内存地址,因为垃圾收集器可以在内存中移动对象,它可能是某种内部对象句柄。 - millimoose
这是大约2008年左右的代码摘录:http://blogs.tedneward.com/CommentView,guid,eca26c5e-307c-4b7c-931b-2eaf5b176e98.aspx - millimoose
直接从消息来源处得知:http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/9b0ca45cd756/src/share/vm/runtime/synchronizer.cpp在我看来,代码与博客文章中的相同。 - millimoose
2
可能是Java中的“内部地址”是什么?的重复问题。 - assylias
3个回答

41

.hashCode()本地实现取决于JVM。

例如,HotSpot有6个Object.hashCode()的实现。您可以使用命令行通过-XX:hashCode=n标志选择它,其中n:

0 - Park-Miller RNG(默认)
1 - f(address,global_statement)
2 - 常量1
3 - 串行计数器
4 - 对象地址
5 - 线程本地Xorshift


3
有趣的是,由于对象头中存储空间有限,因此默认哈希码仅为25位宽,而不是int的完整32位。 - Boann

22

文档中得知:

对于不同的对象,Object类定义的hashCode方法会返回不同的整数,尽可能做到这一点。(通常通过将对象的内部地址转换为整数来实现,但是Java编程语言不要求使用此实现技术。)

因此,它可能与内存地址有关,但不一定是 - 而且绝不能假设它与内存有任何关系。

你对散列码的操作不应该关心这个。你唯一需要了解的是:

  • 如果两个对象的哈希值相同,则它们可能是相等的对象
  • 如果两个对象的哈希值不同,则它们不是相等的对象(无论是否覆盖hashCode方法)

考虑到垃圾回收,哈希码能否作为内存地址?在哈希表中使用时,它不应该在执行期间发生不可预测的变化。 - millimoose
5
在紧凑式垃圾回收(compacting GC)的情况下,它绝对不能是当前的内存地址。但它可能是“第一次调用时的地址,然后为以后记忆”。我尽量不太在意 :) - Jon Skeet
1
根据我找到的旧代码片段,它肯定看起来(就我能读懂有点复杂的C语言而言)是“某个数字,确定一次然后保存”。有六种实现可用,包括初始内存地址和随机数生成器。 - millimoose
2
@GaborSch 首先,哈希码并不能保证唯一性。其次,实现可能并不真正用于“真实”的虚拟机,因为它仍然存在哈希码是(相对较小的)伊甸园空间中地址的问题。 - millimoose
我想补充一下:64位JVM上的内存地址如何给出唯一的32位整数?所以,是的,它不能是内存地址。如果我们在这里看一下:http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/oops/markOop.hpp 我们会发现对于32位机器,哈希码宽度为25位,而在64位机器上宽度为31位。在每个JVM上,您都可以拥有“双胞胎”(具有相同哈希码的不同对象);即使使用-XX:hashCode=5,在Java 8上也可以很快找到双胞胎! - Paul Ianas
显示剩余2条评论

3

你的答案在这里。如文档所述:

尽可能地,由Object类定义的hashCode方法确实会为不同的对象返回不同的整数。(通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言并不要求采用此实现技术。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接