JVM 如何确保 System.IdentityHashCode()永远不变?

通常,Object.hashCode()的默认实现是内存中对象的已分配地址的某个函数(尽管这不是 JLS强制要求的)。假设 VM 分流了内存中的对象,为什么 System.identityHashCode()返回的值在对象的生命周期内从未改变?

如果这是一个“一次性”的计算(对象的 hashCode计算一次并隐藏在对象头部或其他地方) ,那么这是否意味着两个对象有可能拥有相同的 identityHashCode(如果它们碰巧首先在内存中的相同地址分配) ?

14963 次浏览

据我所知,这是为了返回引用而实现的,引用在对象生命周期中永远不会改变。

在回答第二个问题时,不管实现如何,多个对象都可能具有相同的 IdentityHashCode。

有关 javadoc 中的措辞的简要讨论,请参见 窃听器6321873,以及演示非唯一性的程序。

现代 JVM 将值保存在对象头中。我相信这个值通常只在第一次使用时计算,以便将对象分配所花费的时间保持在最低限度(有时甚至低至12个周期)。可以编译公共 Sun JVM,以便所有对象的标识哈希代码始终为1。

多个对象可以有相同的标识哈希代码。这是哈希代码的本质。

实现散列函数的一般准则是:

  • 同一个对象应该返回一致的 hashCode ,它不应该随时间变化或者依赖于任何变量信息(例如一个由随机数或可变成员字段的值播种的算法)
  • 散列函数应该有一个 良好的随机分布,我的意思是,如果您将散列代码视为桶,那么2个对象应该尽可能地映射到不同的桶(散列代码)。两个对象具有相同散列码的可能性应该是罕见的——尽管它发生在 可以上。

HotSpot 中对象的头部由一个类指针和一个“标记”字组成。

标记字的数据结构的源代码可以在 markOop.hpp文件中找到。在这个文件中有一个注释描述了标记字的内存布局:

hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)

在这里,我们可以看到32位系统上普通 Java 对象的标识哈希代码保存在标记字中,它有25位长。