java中Object的默认hashCode方法实现原理

前言

java中哈希码有以下约定:

在同一个java程序执行过程中,不论调用hashCode方法多少次,都要返回相同的值,
两个对象的equals方法相同,hashCode方法一定相同,
两个对象的equals方法不相同,hashCode方法不一定不同,
两个对象的hashCode方法不相同,equals方法一定不同,
两个对象的hashCode方法相同,equals方法不一定相同。

hashCode()在Object中是一个native方法,注释上说是对象的内存地址转换的一个值,那么到底是不是呢,我们以openjdk8源码为例来探究一下。

源码分析

具体的源码追踪过程可以看 How does the default hashCode() work?源码入口

// src/share/vm/prims/jvm.cpp
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
   JVMWrapper("JVM_IHashCode");
   // as implemented in the classic virtual machine; return 0 if object is NULL
   return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END
// 这里是作者简化过的伪码
// src/share/vm/runtime/synchronizer.cpp
intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {
    mark = monitor->header();
    ...
    hash = mark->hash();
    if (hash == 0) {
    hash = get_next_hash(Self, obj);
    ...
    }
    ...
    return hash;
}
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     value = os::random() ;
  } else
  if (hashCode == 1) {
     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 ;
  }
...
  return value;
}
product(intx, hashCode, 5,\
    "(Unstable) select hashCode generation algorithm") 
// src/share/vm/runtime/thread.cpp
  _hashStateX = os::random() ;
  _hashStateY = 842502087 ;
  _hashStateZ = 0x8767 ;    // (int)(3579807591LL & 0xffff) ;
  _hashStateW = 273326509 ;

get_next_hash()方法一共提供了六种实现

0. 随机数
1. 内存地址做移位再和一个随机数做异或
2. 固定值1
3. 自增序列的当前值
4. 内存地址
5. 当前线程有关的一个随机数+三个确定值,运用xorshift随机数算法得到的一个随机数

默认使用的5,第六种实现,和内存地址是无关的,我们也可以通过在JVM启动参数中添加-XX:hashCode=4,改变默认的hashCode计算方式。
一个对象创建了哈希码之后会将值保存到对象的对象头中,避免下次创建,在垃圾回收过程中,哈希码也不会改变。

参考

开发中常见的一些Hash函数(一)
How does the default hashCode() work?
Java Object.hashCode()返回的是对象内存地址?

posted @ 2021-03-08 19:14  strongmore  阅读(3041)  评论(0编辑  收藏  举报