之前在 Serializable 一文中简单提及了 Object.hashCode()
方法的实现,今天在看 IdentityHashMap
的源码时看到对 System.identityHashCode()
方法的调用,本文简要记录。
首先在 System.identityHashCode()
的 Java Doc 中有如下下说明:
Returns the same hash code for the given object as would be returned by the default method hashCode(), whether or not the given object’s class overrides hashCode(). The hash code for the null reference is zero.
即与 Object.hashCode()
返回的值相同,可以根据 Open JDK 中的源码来验证,比如在 System.c at jdk8-b120 中有如下代码:
1 | JNIEXPORT jint JNICALL |
在 Object.c at jdk8-b120 中有如下代码:
1 | static JNINativeMethod methods[] = { |
可以看出 System.identityHashCode()
与 Object.hashCode()
都是调用的 JVM_IHashCode()
方法,该方法的定义位于 jvm.h at jdk8-b120,实现位于 jdk/jvm.cpp at jdk8-b120:
1 | JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle)) |
跟随源码至 synchronizer.cpp at jdk8-b120:
1 | intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) { |
根据以上源码可知,如果当前对象偏向了某线程,则取消该偏向锁,然后检查对象头上是否含有 hash 值,如果含有则直接返回,如果不含有则调用 get_next_hash()
方法生成 hash 值设置至对象头中并返回,该方法实现位于 synchronizer.cpp at jdk8-b120:
1 | // hashCode() generation : |
可以看出源码中提供了六种 hashCode()
方法的实现,在 JDK 8 中执行 java -XX:+PrintFlagsFinal -version | grep hashCode
的输出值如下:
1 | intx hashCode = 5 {product} |
可以知道 JDK 8 中使用的 Marsaglia’s shift-xor RNG scheme 的变体实现。其中注释提到:
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.
即生成的值与线程特定状态有关,如果我们编写如下代码:
1 | package me.tianshuang; |
多次运行以上的代码,可以看出每次输出的值相同。而为何未采用基于内存地址的实现呢,其主要原因为 TLAB 的存在会导致连续创建的对象具有连续的内存地址,导致哈希分布不均,全局计数器具有类似的问题,常量哈希值就更差了,直接导致哈希碰撞。
System.identityHashCode()
在哪些地方有用到呢?我举两个例子,一个是 IdentityHashMap 中对 hash 值的计算,源码位于 IdentityHashMap.java at jdk8-b120:
1 | /** |
此处使用了 System.identityHashCode()
而未使用 key
的 hashCode()
方法。另一个例子可以参考 HashMap
链表转换为红黑树后,对于不能通过 Comparable
进行比较或者比较后 compareTo
返回值为 0 的情况,会尝试比较 key
的类名,如果类名相同,则使用 System.identityHashCode()
进行比较,源码位于 HashMap.java at jdk8-b120:
1 | /** |
Reference
Where is object’s hash code stored if biased-locking is enabled in HotSpot JVM? - Stack Overflow
JVM Anatomy Quark #26: Identity Hash Code
IdentityHashMap