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