两个不同的Class实例给出相同的hashCode

时间:2015-09-02 03:26:07

标签: java ognl

我在JBoss服务器上遇到一个奇怪的问题,其中两个类产生相同的hashCode()

Class<?> cl1 = Class.forName("fqn.Class1");
Class<?> cl2 = Class.forName("fqn.Class2");
out.println(cl1.getCanonicalName());
out.println(cl2.getCanonicalName());
out.println(cl1.hashCode());
out.println(cl2.hashCode());
out.println(System.identityHashCode(cl1));
out.println(System.identityHashCode(cl2));
out.println(cl1 == cl2);
out.println(cl1.equals(cl2));
out.println(cl1.getClassLoader().equals(cl2.getClassLoader()));

产地:

fnq.Class1
fnq.Class2
494722
494722
494722
494722
false
false
true

我通常不会关心,但我们正在使用一个框架,该框架使用由类中的哈希码和属性名称组成的密钥来缓存setter。它是一个糟糕的缓存设计,但目前我无法控制(最新的Struts 2.3.24中的OGNL 3.0.6,请参阅source。更新的OGNL解决了这个问题,但是它不会在Struts中使用,直到2.5,目前处于测试阶段。)

这个问题对我来说有点奇怪是

  • 使用几天后出现问题......我非常确定这两个类/属性在此期间都会被缓存。这让我相信类实例哈希码实际上是更改 ......几天后它们变得相等。
  • 我们观察了一个非常过时的Hotspot 1.6的行为,现在是1.7.0_80。两者都是Sun Sparc上的32位版本
  • JVM报告-XX:hashCode为&#34; 0&#34;

我读到Hotspot中的RNG哈希码生成器(&#34; 0&#34;策略)如果有赛车线程可以产生重复,但是我无法想象类加载会触发这种行为。 / p>

在创建Class实例时,Hotspot是否使用特殊的哈希码处理?

1 个答案:

答案 0 :(得分:5)

  1. java.lang.Class不会覆盖hashCode,JVM也不会特别处理它。这只是从java.lang.Object继承的常规标识hashCode。
  2. 当使用-XX:hashCode=0(JDK 6和JDK 7中的默认值)时,使用全局Park-Miller随机数生成器计算标识hashCode。此算法生成周期为2^31-2的唯一整数,因此除了以下原因外,两个对象几乎不可能具有相同的hashCode。
  3. 由于此算法依赖于未同步的全局变量,因此竞争条件(the source)确实存在两个不同线程生成相同随机数的可能性。这就是你的情况所发生的事情。
  4. 标识hashCode不是在对象创建时生成的,而是在第一次调用hashCode方法时生成的。因此,何时以及如何加载类并不重要。如果同时调用hashCode,则任何两个对象都可能发生此问题。
  5. 我建议使用-XX:hashCode=5(默认为JDK 8)。此选项使用线程本地Xorshift RNG。它不受竞争条件的影响,也比Park-Miller算法快。