不同对象identityHashCode一定不同吗

转自

https://www.jianshu.com/p/24fa4bdb9b9d

https://zhuanlan.zhihu.com/p/28270828

https://hllvm-group.iteye.com/group/topic/39183

https://www.jianshu.com/p/be943b4958f4

 

总结:

1. 有6种方法生成hashcode,jdk8后默认第5种--xor-shift随机数生成

0 - 使用Park-Miller伪随机数生成器(跟地址无关)
1 - 使用地址与一个随机数做异或(地址是输入因素的一部分)
2 - 总是返回常量1作为所有对象的identity hash code(跟地址无关)
3 - 使用全局的递增数列(跟地址无关)
4 - 使用对象地址的“当前”地址来作为它的identity hash code(就是当前地址)
5 - 使用线程局部状态来实现Marsaglia's xor-shift随机数生成(跟地址无关,默认)
 
2. 由于是随机数,所以可能相同
 (1)一个对象在其生命期中 identity hash code 必定保持不变 (Java对象会在首次真正使用到它的identity hash code时调用VM里的函数来计算出值,然后保存在对象头部的markword);

(2)如果a == b,那么他们的System.identityHashCode() 必须相等
    如果他们的System.identityHashCode() 不相等,那他们必定不是同一个对象(逆否命题与原命题真实性总是相同);

(3)如果System.identityHashCode() 相等的话,并不能保证 a == b
 
3. Xorshift 随机数生成器
 
Xorshift 随机数生成器是 George Marsaglia 发明的一类伪随机数生成器。它们通过和自己逻辑移位后的数进行异或操作来生成序列中的下一个数。这在现代计算机体系结构非常快。它们是线性反馈移位寄存器的一个子类,其简单的实现使它们速度更快且使用更少的空间。然而,必须仔细选择合适参数以达到长周期。

Xorshift生成器是非密码安全的随机数生成器中最快的一种,只需要非常短的代码和状态。虽然它们没有进一步改进以通过统计检验,这个缺点非常著名且容易修改(Marsaglia 在原来的论文中指出),用复合一个非线性函数的方式,可以得到比如像 xorshift+ 或 xorshift* 生成器。一个简单的C语言实现的 xorshift+ 生成器通过了所有的 BigCrush 的测试(比 Mersenne Twister 算法和 WELL 算法的失败次数减少了一个数量级),而且在 x86 上产生一个随机数通常只需要不到十个时钟周期,多亏了指令流水线。
 
 
4. 调用identity hash code使偏向锁失效

当一个对象已经计算过identity hash code,它就无法进入偏向锁状态;当一个对象当前正处于偏向锁状态,并且需要计算其identity hash code的话,则它的偏向锁会被撤销,并且锁会膨胀为重量级锁;

那什么时候对象会计算identity hash code呢?当然是当你调用未覆盖的Object.hashCode()方法或者System.identityHashCode(Object o)时候了。

 

 
5. 源码:
// hashCode() generation :
//
// Possibilities:
// * MD5Digest of {obj,stwRandom}
// * CRC32 of {obj,stwRandom} or any linear-feedback shift register function.
// * A DES- or AES-style SBox[] mechanism
// * One of the Phi-based schemes, such as:
//   2654435761 = 2^32 * Phi (golden ratio)
//   HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ;
// * A variation of Marsaglia's shift-xor RNG scheme.
// * (obj ^ stwRandom) is appealing, but can result
//   in undesirable regularity in the hashCode values of adjacent objects
//   (objects allocated back-to-back, in particular).  This could potentially
//   result in hashtable collisions and reduced hashtable efficiency.
//   There are simple ways to "diffuse" the middle address bits over the
//   generated hashCode values:

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0;
  if (hashCode == 0) {
    // This form uses global Park-Miller 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();
  } else 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;
  } else if (hashCode == 2) {
    value = 1;            // for sensitivity testing
  } else if (hashCode == 3) {
    value = ++GVars.hcSequence;
  } else if (hashCode == 4) {
    value = cast_from_oop<intptr_t>(obj);
  } else {
    // Marsaglia's xor-shift scheme with thread-specific state
    // This is probably the best overall implementation -- we'll
    // likely make this the default in future releases.
    unsigned t = Self->_hashStateX;
    t ^= (t << 11);
    Self->_hashStateX = Self->_hashStateY;
    Self->_hashStateY = Self->_hashStateZ;
    Self->_hashStateZ = Self->_hashStateW;
    unsigned v = Self->_hashStateW;
    v = (v ^ (v >> 19)) ^ (t ^ (t >> 8));
    Self->_hashStateW = v;
    value = v;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD;
  assert(value != markOopDesc::no_hash, "invariant");
  TEVENT(hashCode: GENERATE);
  return value;
}

 

  // thread-specific hashCode stream generator state - Marsaglia shift-xor form
  _hashStateX = os::random();
  _hashStateY = 842502087;
  _hashStateZ = 0x8767;    // (int)(3579807591LL & 0xffff) ;
  _hashStateW = 273326509;

 

int os::random() {
  // Make updating the random seed thread safe.
  while (true) {
    unsigned int seed = _rand_seed;
    unsigned int rand = random_helper(seed);
    if (Atomic::cmpxchg(rand, &_rand_seed, seed) == seed) {
      return static_cast<int>(rand);
    }
  }
}

 

volatile unsigned int os::_rand_seed      = 1;

 

由于种子初值是一样的,所以会出现JVM每次启动时生成 Object 对象的 hashcode 的序列是一样的,在不同机器上也是如此。

 

第一次实验

public static void main(String[] args) {
        Object obj = new Object();
        Object obj2 = new Object();
        System.out.println(obj.hashCode());
        System.out.println(obj2.hashCode());
}

 

结果:

 

 

第二次实验

class Node{
    int a = 1;
    int b = 2;
}

 

public static void main(String[] args) {
        Node node = new Node();
        System.out.println(Node.class.hashCode());
        System.out.println(node.hashCode());
    }

 

结果:

 

 

 

 

 

 

posted @ 2020-09-12 16:00  Numerz  阅读(405)  评论(0编辑  收藏  举报