探索hotspot的hashCode源码实现

371 阅读3分钟

看抖音有人问Object#hashcode方法实现,以前面试刷过文章大概记得说是对象的地址,今天从源码一探究竟。

1.jdk源码以及jvm源码下载

jdk源代码相当于jdk接口,而jvm源码相当于jdk接口的实现。我们要找的hashcode方法需要先到jdk源代码找到对应到jvm代码的方法名称,在到jvm源码找到实现。

首先下载两个源代码,网速不好可以用gitee:

源码githubgitee
jdk8github.com/gorden5566/…gitee.com/gorden5566/…
jvm-hotspot8github.com/gorden5566/…gitee.com/gorden5566/…

2.从jdk源码中找到hashcode的native定义

下载jdk8代码,用vscode打开,定位到下面目录。

image.png

找到Object.c文件,可以看到hashCode方法对应的native方法名称是JVM_IHashCode,下面转到jvm源码找到c方法实现。

3.从jvm源码中找到JVM_IHashCode方法实现

下载完jvm-hotspot后,使用vscode打开,使用用全局搜索功能搜索方法名称JVM_IHashCode,可以直接定位到源码实现:

image.png

4.探索hashCode的C代码实现

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

4.1 jobject是个什么鬼

第一行代码是个判空,所以必须要知道这个参数怎么来的。

我们知道每一个jdk的native方法其实都对应到c语言的一个方法,那么方法的参数也是一一对应么?实际确实如此,但是需要补充this等等参数。

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( 
     JNIEnv *env,        /* 接口指针 */ 
     jobject obj,        /* this 对象指针 */ 
     jint i,             /* 参数 1 */ 
     jstring s)          /* 参数 2 */ 
{ 
     /* 复制 java 的 String */ 
     const char *str = (*env)->GetStringUTFChars(env, s, 0); 

     /* 处理 string */ 
     ... 

     /* 处理完毕 */ 
     (*env)->ReleaseStringUTFChars(env, s, str); 

     return ... 
} 

所以对于代码handle == NULL相信你有答案了。

4.2 ObjectSynchronizer::FastHashCode探索

ok,我们探索hashCode源码的目标转为了探索ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle))的源码。

不啰里八嗦打字了,直接上图:

image.png

下一步就是探索对象头的hash如何生成,个人猜测两个情况:

  1. 创建对象头时候一起生成
  2. 第一次调用hashCode生成

4.3 对象头中hash值生成

我们先按照上面第二个猜测继续看下源码。OMG是我疏忽了,下面一行代码就已经给了答案,验证了第二个猜测是正确的:

image.png

4.4 生成hash

进入get_next_hash方法。 image.png

结合注释和代码,我发现hotspot生成hash的花样真是多啊,具体用哪个方法生成又是依靠hashCode这个变量判断,依赖vscode,直接跳转定义:

image.png

image.png

最后来到hashCode类型定义位置,说实话我看不懂最后是不是5,姑且当做5判断。

image.png

image.png

到这里就完了,怎么算的我从来不去纠结,只要运算结果符合hashCode定义即可:

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

查了一下资料,可以通过jvm参数指定变量hashCode的值XX:hashCode=4