如何在CLR&中实现Object.GetHashCode()。 JVM?

时间:2011-04-07 13:15:07

标签: mono jvm clr low-level

我一直在思考这个问题:在CLR或Java中如何实现Object.GetHashCode?此方法的合同是,如果在同一对象实例上调用它,则应始终返回相同的值。

请注意,我在谈论GetHashCode()的默认实现。派生类不需要覆盖此方法。如果他们选择不这样做,那么它们本质上将具有引用语义:当在哈希表& c中使用时,默认等于“指针相等”。这意味着不知何故,运行时必须在整个生命周期内为对象提供一个常量哈希码。

如果我正在运行的机器是32位,并且如果对象实例从未在内存中移动,理论上可以返回对象的地址,重新解释为Int32。这样会很好,因为所有不同的对象都有不同的地址,因此会有不同的哈希码。

然而,这种方法存在缺陷,其中包括:

  • 如果垃圾收集器将对象移动到内存中,则其地址会发生变化,违反协议的哈希码也会在对象的生命周期内与哈希码相同。

    < / LI>
  • 在64位系统上,对象的地址太宽,无法容纳Int32。

  • 由于托管对象倾向于与某个偶数幂2对齐,因此最底部的位始终为零。当哈希码用于索引到哈希表时,这可能会导致错误的分发模式。

在.NET中,System.Object由同步块和类型句柄组成,所以哈希码不能在实例本身中缓存。不知何故,运行时能够提供持久的哈希码。怎么样? Java,Mono和其他运行时如何做到这一点?

3 个答案:

答案 0 :(得分:9)

不,不是地址,不能与垃圾收集器移动对象一起使用。它直观简单,只要在生成后存储就可以是随机数。它 存储在对象syncblk中。该字段存储多个对象属性,如果需要存储多个此类属性,则将其替换为已分配的syncblk的索引。

.NET算法使用托管线程ID,以便线程不可能生成相同的序列:

inline DWORD GetNewHashCode()
{
    // Every thread has its own generator for hash codes so that we won't get into a situation
    // where two threads consistently give out the same hash codes.        
    // Choice of multiplier guarantees period of 2**32 - see Knuth Vol 2 p16 (3.2.1.2 Theorem A)
    DWORD multiplier = m_ThreadId*4 + 5;
    m_dwHashCodeSeed = m_dwHashCodeSeed*multiplier + 1;
    return m_dwHashCodeSeed;
}

种子存储在每个线程中,因此不需要锁定。至少这是SSCLI20版本中使用的内容。不知道Java,我想它是相似的。

答案 1 :(得分:4)

作为JVM实现者,我可以说基本哈希码IS通常与对象的地址相关。它通常不是地址,而是以合理的方式对其进行一些修改。我们做魔术以确保hashCode在对象的生命周期内是稳定的(即使在GC中,即使对象移动等等)。

我强烈建议为要进行散列的所有对象实现一个特定类型的hashCode()。该Object实现它并不意味着它是您使用的理想选择。

答案 2 :(得分:0)

我不确定你的意思是“在CLR或Java中究竟是如何实现Object.GetHashCode?”。 Java的“public int hashCode()”具有类的作者应该为其定义hashCode()实现的契约。换句话说,它可能在不同类别之间变化很大。我怀疑.Net平台也是如此。

Javadoc for Object描述了一种类似于您的想法的方法: http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Object.html#hashCode()

  

尽可能合理,   class定义的hashCode方法   对象确实返回不同的整数   对于不同的对象。 (这是   通常通过转换实现   对象的内部地址   变成一个整数,但是这个   实施技术不是   JavaTM编程所要求的   语言。)

如果您为类定义了基于身份以外的其他内容的相等性,则此方法不适用。