.hashcode() 会由于任期空间的压缩而返回不同的 int 吗?

2022-09-01 03:10:31

如果我在某个对象上调用该方法,它将返回该对象的内部地址(默认实现)。此地址是逻辑地址还是物理地址?Object.hashcode()

在垃圾回收中,由于内存压缩,对象移位发生在内存中。如果我在GC之前和之后调用哈希码,它会返回相同的哈希码(它返回)吗?如果是,那么为什么(因为压缩地址可能会改变)?


答案 1

@erickson或多或少是正确的。返回的哈希码在对象的生存期内不会更改。java.lang.Object.hashCode()

这(通常)实现的方式相当聪明。当垃圾回收器重新定位对象时,其原始哈希码必须存储在某个地方,以防再次使用。实现此目的的明显方法是向对象标头添加一个 32 位字段以保存哈希码。但这会给每个对象增加一个单词的开销,并且在最常见的情况下会浪费空间......其中不调用对象的方法。hashCode

解决方案是向对象的标志词添加两个标志位,并按如下方式(粗略地)使用它们。第一个标志是在调用该方法时设置的。第二个标志告诉该方法是使用对象的当前地址作为哈希码,还是使用存储的值。当 GC 运行并重新定位对象时,它会测试这些标志。如果设置了第一个标志,而未设置第二个标志,则 GC 会在对象的末尾分配一个额外的单词,并将原始对象位置存储在该单词中。然后它设置两个标志。从那时起,该方法从对象末尾的单词中获取哈希码值。hashCodehashCodehashCode


实际上,实现必须以这种方式运行,以满足常规哈希码协定的以下部分:identityHashCode

“每当在 Java 应用程序执行期间在同一对象上多次调用它时,hashCode 方法必须始终返回相同的整数,前提是不修改对象的相等比较中使用的信息。此整数不必从应用程序的一次执行到同一应用程序的另一次执行保持一致。

如果/当GC将对象移动到其他地址时,简单地返回对象的当前计算机地址的假设实现将违反突出显示的部分。解决这个问题的唯一方法是让(假设的)JVM保证一个对象永远不会移动一次。这将导致堆碎片化的严重和棘手的问题。identityHashCode()hashCode


答案 2

否,对象的默认哈希代码不会更改。

文档没有说哈希代码地址,而是说它基于地址。考虑到哈希代码是32位,但有64位JVM。显然,直接使用地址并不总是有效。

实现取决于JVM,但在Sun(Oracle)JVM中,我相信哈希代码在第一次被访问时就会被缓存。