我的问题是: Object类的默认哈希码实现是否使用identityhashcode?我认为确实如此。如果没有,请纠正我。
假设它确实存在我的问题:让我们说一个对象的hashCode()被调用一次在堆压缩期间(并将identityhashcode与它一起存储),并在第一个对象的旧位置创建一个新的不同对象。在这种情况下,即使对象不同,对象的identityhashcode也是相同的。怎么解释这个?
答案 0 :(得分:2)
根据我的理解,一些Java实现通过保留对象头的两位来实现identityHashCode
,将对象划分为三类:
从未调用identityHashCode()
的那些。
在对象移动的最近时间之后首次调用identityHashCode()
的那些。
第一次调用identityHashCode()
的对象发生在对象移动的最近时间之前。
对于第一类对象,对identityHashCode()
的调用将返回与对象地址相关的值,并设置一个标志以将对象更改为第二类。对于第二类中的对象,identityHashCode()
将从第一次调用的地址计算相同的值。 GC每次移动一个对象时,都会检查它是否在上面的第二个类别中。如果是这样,GC会在目标位置保留额外的四个字节,用于保存对象移动前的标识哈希值。
如果对象具有其哈希码,则下次GC必须移动时,对象的有效大小将增加4个字节。但是,大多数对象从未使用其标识哈希码,并且其中一些对象具有其身份哈希码,但会立即收集。让对象第一次调用identityHashCode()
为对象分配额外的四个字节将是困难的,但是当移动对象时分配额外的四个字节不是问题。在第一次调用identityHashCode()
和下次对象移动之间使用对象的地址作为其哈希值,避免了在使用对象后面的空间时分配存储的需要。
请注意,虽然身份哈希方法可以“合法地”直接使用地址的位模式,但这样做可能会导致过多的哈希重复(例如,在一个GC周期后创建的第一个对象可能很容易具有与第一个对象创建一个接一个)。避免该问题的一种简单方法是让系统添加,xor或以其他方式将地址与在每个GC周期后变化的值组合。这种方法可以很容易地将散列值扩展到更广泛的范围内。
答案 1 :(得分:1)
关键文件是hashCode
的{{3}}。它说,部分"每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象的equals比较中使用的信息"
在Object
的情况下,相等性不依赖于对象中的任何信息,只取决于两个引用是否指向同一个对象,因此没有条件可以更改哈希代码。
文档还说"尽管合理可行,但是由Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但Java™编程语言不需要此实现技术。)"
为了符合合同,"内部地址"必须是在整个生命周期中与对象保持一致的东西,而不是可能受到堆压缩影响的东西。
identityHashCode
是根据Object
哈希码定义的,因此这也会控制其行为。