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()返回的是對象內存地址?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM