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