Java 为什么jvm中的默认哈希代码生成在JDK 8中切换为xor shift?

Java 为什么jvm中的默认哈希代码生成在JDK 8中切换为xor shift?,java,jvm,Java,Jvm,我正在学习JVM代码,以便更深入地理解Java。有一条评论说: //具有线程特定状态的Marsaglia的xor移位方案 //这可能是最好的总体实现--我们将 //这可能会成为未来版本中的默认设置 当变量hashcode不是(0,1,2,3,4)中的任何一个时,可以在in-else分支中找到这一点。可以通过JVM选项“-XX:hashcode=n”设置此变量 我编写了一些代码来测试这些散列算法: public static void main(String[] args) { long

我正在学习JVM代码,以便更深入地理解Java。有一条评论说:

//具有线程特定状态的Marsaglia的xor移位方案
//这可能是最好的总体实现--我们将
//这可能会成为未来版本中的默认设置

当变量
hashcode
不是(0,1,2,3,4)中的任何一个时,可以在in-else分支中找到这一点。可以通过JVM选项“-XX:hashcode=n”设置此变量

我编写了一些代码来测试这些散列算法:

public static void main(String[] args) {
    long now = System.currentTimeMillis();
    RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
    List<String> arguments = runtimeMxBean.getInputArguments();
    for(String s:arguments){
        System.out.println(s);
    }

    HashMap<Integer,Object> h = new HashMap<>();

    ArrayList<Object> arrayList = new ArrayList<>();

    for(int i=0;i<2000000;i++){
        Object o = new Object();
        if(h.containsKey(o.hashCode())){
            arrayList.add(o);
            continue;
        }
        h.put(o.hashCode(),o);
    }
    long currentTimeMillis = System.currentTimeMillis();
    System.err.println("hashcode collision:"+arrayList.size());
    System.err.println(" used time "+(currentTimeMillis - now));

}

然后我将时间设置为20000000,addr XOR SHIFT仍然是0。我的问题是:它比xor移位更好吗?为什么jdk-8将“-XX:hashcode=5”作为默认值?

好的哈希函数的属性包括1)随机性、2)一致性、3)性能和4)可伸缩性。少量的冲突并不意味着哈希函数足够随机,例如,在您的测试中,顺序哈希代码也不会产生冲突,但显然这不是一个好的哈希函数

此外,您只测试了一个单线程案例。对于单个线程,
-XX:hashCode=0
(在JDK 8之前默认的Park Miller RNG算法)的性能相当好。但是,在高度并发的应用程序中,它变得非常糟糕:由于全局变量上的高争用,性能变得很差,并且由于争用条件,在不同线程中生成相同哈希代码的机会增加,请参见源代码中的:

  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = os::random() ;
-XX:hashCode=1
在随机性方面也远远不够完美。它只是使用仅在停止时更新的全局变量对对象上的地址进行异或,而世界JVM暂停:

  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
if(hashCode==1){
//这种变化具有稳定(幂等)的性质
//在STW操作之间。这在某些1-0操作中非常有用
//同步方案。
intptr\u t addrBits=从oop(obj)>>3投下;
value=addrBits^(addrBits>>5)^GVars.strandom;
您可以在中找到对不同哈希代码算法的讨论和分析


简而言之,只有
-XX:hashCode=0
-XX:hashCode=5
提供了良好的随机性,而后者更具可扩展性和性能,因为它只使用简单的按位操作,不更新全局变量。

没有冲突并不是哈希函数排名的唯一标准。执行速度和可预测性是另外两个潜在指标。我不知道为什么会更改默认值,但我认为这是这些考虑因素和其他一些因素的混合。谢谢你的回答,我花了很长时间才理解,你的意思是hashcode=1缺乏随机性,在我看来,与hashcode=1和hashcode=5的主要区别是随机数GVars.strandom仅在STW发生时更改,XOR-SHIFT的随机数在Self(vmThread)时设置创建时,这提供了hashCode生成函数的随机性?所以在我的单线程测试中,两个函数的随机性并没有太多differences@Cheng.T
-XX:hashCode=1
根据对象地址生成值。因此,在STW暂停之间,可能的hashCode范围相当有限,这取决于堆大小和GC算法m、 相反,
-XX:hashCode=5
使用PRNG生成整个正整数范围内的值。
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;