java Object的hashCode方法怎么使用

java Object的hashCode方法怎么使用

这篇文章主要讲解了“javaObject的hashCode方法怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“javaObject的hashCode方法怎么使用”吧!

    1. 背景介绍

    在为重写hashCode方法的时候,看到hashCode打印出的数据像是一个地址值,很是好奇。

    java Object的hashCode方法怎么使用

    加之最近在研读jvm源码,特此一探究竟,看看在hotspot中hashCode究竟是如何实现的。

    2. 调用过程梳理

    java的Object代码

    publicnativeinthashCode();

    通过官产jdk的Object.class的源码, 发现hashCode被native修饰. 因此这个方法应该是在jvm中通过c/c++实现

    jvm的hashCode相关代码

    首先观察Object.java对应的Object.c代码

    //文件路径:jdk\src\share\native\java\lang\Object.cstaticJNINativeMethodmethods[]={{"hashCode","()I",(void*)&JVM_IHashCode},//这个方法就是我们想看的hashCode方法{"wait","(J)V",(void*)&JVM_MonitorWait},{"notify","()V",(void*)&JVM_MonitorNotify},{"notifyAll","()V",(void*)&JVM_MonitorNotifyAll},{"clone","()Ljava/lang/Object;",(void*)&JVM_Clone},};

    进一步进入到jvm.h文件中, 这个文件中包含了很多java调用native方法的接口

    //hotspot\src\share\vm\prims\jvm.h/**java.lang.Object*/JNIEXPORTjintJNICALLJVM_IHashCode(JNIEnv*env,jobjectobj);//此时定了已hashCode方法的接口,具体实现在jvm.cpp中

    //hotspot\src\share\vm\prims\jvm.cpp//java.lang.Object///JVM_ENTRY(jint,JVM_IHashCode(JNIEnv*env,jobjecthandle))JVMWrapper("JVM_IHashCode");//asimplementedintheclassicvirtualmachine;return0ifobjectisNULLreturnhandle==NULL?0:ObjectSynchronizer::FastHashCode(THREAD,JNIHandles::resolve_non_null(handle));//如果object为null,就返回0;否则就调用ObjectSynchronizer::FastHashCodeJVM_END

    进入到ObjectSynchronizer::FastHashCode

    //hotspot\src\share\vm\runtime\synchronizer.cppintptr_tObjectSynchronizer::FastHashCode(Thread*Self,oopobj){//....//在FastHashCode方法中有一段关键代码:if(mark->is_neutral()){hash=mark->hash();//首先通过对象的markword中取出hashCodeif(hash){//如果取调到了,就直接返回returnhash;}hash=get_next_hash(Self,obj);//如果markword中没有设置hashCode,则调用get_next_hash生成hashCodetemp=mark->copy_set_hash(hash);//生成的hashCode设置到markword中//use(machinewordversion)atomicoperationtoinstallthehashtest=(markOop)Atomic::cmpxchg_ptr(temp,obj->mark_addr(),mark);if(test==mark){returnhash;}}//....}

    生成hashCode的方法get_next_hash, 可以支持通过参数配置不同的生成hashCode策略

    //hotspot\src\share\vm\runtime\synchronizer.cppstaticinlineintptr_tget_next_hash(Thread*Self,oopobj){intptr_tvalue=0;//一共支持6中生成hashCode策略,默认策略值是5if(hashCode==0){//策略1:直接通过随机数生成value=os::random();}elseif(hashCode==1){//策略2:通过object地址和随机数运算生成intptr_taddrBits=cast_from_oop<intptr_t>(obj)>>3;value=addrBits^(addrBits>>5)^GVars.stwRandom;}elseif(hashCode==2){//策略3:永远返回1,用于测试value=1;//forsensitivitytesting}elseif(hashCode==3){//策略4:返回一个全局递增的序列数value=++GVars.hcSequence;}elseif(hashCode==4){//策略5:直接采用object的地址值value=cast_from_oop<intptr_t>(obj);}else{//策略6:通过在每个线程中的四个变量:_hashStateX,_hashStateY,_hashStateZ,_hashStateW//组合运算出hashCode值,根据计算结果同步修改这个四个值unsignedt=Self->_hashStateX;t^=(t<<11);Self->_hashStateX=Self->_hashStateY;Self->_hashStateY=Self->_hashStateZ;Self->_hashStateZ=Self->_hashStateW;unsignedv=Self->_hashStateW;v=(v^(v>>19))^(t^(t>>8));Self->_hashStateW=v;value=v;}value&=markOopDesc::hash_mask;//通过hashCode的mask获得最终的hashCode值if(value==0)value=0xBAD;assert(value!=markOopDesc::no_hash,"invariant");TEVENT(hashCode:GENERATE);returnvalue;}

    3. 关于hashCode值的大小

    前面以及提交到hashCode生成后, 是存储在markword中, 我们在深入看一下这个markword

    //hotspot\src\share\vm\oops\markOop.hppclassmarkOopDesc:publicoopDesc{private://Conversionuintptr_tvalue()const{return(uintptr_t)this;}public://Constantsenum{age_bits=4,lock_bits=2,biased_lock_bits=1,max_hash_bits=BitsPerWord-age_bits-lock_bits-biased_lock_bits,hash_bits=max_hash_bits>31?31:max_hash_bits,//通过这个定义可知,hashcode可占用31位bit.在32位jvm中,hashCode占用25位cms_bits=LP64_ONLY(1)NOT_LP64(0),epoch_bits=2};}

    4. 验证

    packagetest;/****可以通过系列参数指定hashCode生成策略*-XX:hashCode=2*/publicclassTestHashCode{publicstaticvoidmain(String[]args){Objectobj1=newObject();Objectobj2=newObject();System.out.println(obj1.hashCode());System.out.println(obj2.hashCode());}}

    通过-XX:hashCode=2这种形式, 可以验证上述的5中hashCode生成策略

    5. 总结

    在64位jvm中, hashCode最大占用31个bit; 32位jvm中, hashCode最大占用25个bit

    hashCode一共有六种生成策略

    序号hashCode策略值描述
    10直接通过随机数生成
    21通过object地址和随机数运算生成
    32永远返回1, 用于测试
    43返回一个全局递增的序列数
    54直接采用object的地址值
    6其他通过在每个线程中的四个变量: _hashStateX, _hashStateY, _hashStateZ, _hashStateW 组合运算出hashCode值, 根据计算结果后修改这个四个值

    默认策略采用策略6, 在globals.hpp文件中定义

    product(intx,hashCode,5,\"(Unstable)selecthashCodegenerationalgorithm")

    感谢各位的阅读,以上就是“javaObject的hashCode方法怎么使用”的内容了,经过本文的学习后,相信大家对javaObject的hashCode方法怎么使用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是恰卡编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

    发布于 2021-12-22 21:55:11
    收藏
    分享
    海报
    0 条评论
    47
    上一篇:C++如何实现堆排序 下一篇:如何用C语言实现圣诞树
    目录

      0 条评论

      本站已关闭游客评论,请登录或者注册后再评论吧~

      忘记密码?

      图形验证码