当前位置: 首页 > 后端技术 > Java

美团方面:hashCode和对象的内存地址是什么关系?我傻眼了,.

时间:2023-04-02 01:01:28 Java

来源:juejin.cn/post/6971946031764209678先看最简单的printSystem.out.println(newObject());会输出类的完全限定类名和一串字符串:java.lang.Object@6659c656@符号后面是什么?它是对象的哈希码还是内存地址?还是其他一些值?其实@后面只是对象的hashcode值,只是16进制显示的hashcode,我们来验证一下:Objecto=newObject();inthashcode=o.hashCode();//toStringSystem.out.println(o);//hashcode十六进制System.out.println(Integer.toHexString(hashcode));//hashcodeSystem.out.println(hashcode);//这个方法也是获取对象的hashcode;但是不同于Object.hashcode的,这个方法会忽略重写的hashcodeSystem.out.println(System.identityHashCode(o));输出结果:java.lang.Object@6659c6566659c65617171595101717159510对象的hashcode是如何生成的?它真的是内存地址吗?本文内容基于JAVA8HotSpothashCode的生成逻辑。JVM中生成hashCode的逻辑并没有那么简单。它提供了几种策略,每种策略产生的结果是不同的。来看一下openjdk源码里生成hashCode的核心方法:staticinlineintptr_tget_next_hash(Thread*Self,oopobj){intptr_tvalue=0;if(hashCode==0){//这种形式使用了一个不受保护的全局Park-MillerRNG,//所以两个线程有??可能竞争并生成相同的RNG。//在MP系统上,我们将对全局进行大量RW访问,因此//机制会导致大量一致性流量。值=os::随机();}elseif(hashCode==1){//这种变体具有在STW操作之间稳定(幂等)的特性。这在某些1-0//同步方案中很有用。intptr_taddrBits=intptr_t(obj)>>3;value=addrBits^(addrBits>>5)^GVars.stwRandom;}elseif(hashCode==2){value=1;//用于敏感性测试}elseif(hashCode==3){value=++GVars.hcSequence;}elseif(hashCode==4){value=intptr_t(obj);}别的{//Marsaglia的具有线程特定状态的xor-shift方案//这可能是最好的整体实现——我们可能会在未来的版本中将其设为默认值。unsignedt=Self->_hashStateX;t^=(t<<11);自我->_hashStateX=自我->_hashStateY;Self->_hashStateY=Self->_hashStateZ;自我->_hashStateZ=自我->_hashStateW;unsignedv=Self->_hashStateW;v=(v^(v>>19))^(t^(t>>8));self->_hashStateW=v;价值=v;}值&=markOopDesc::hash_mask;如果(值==0)值=0xBAD;assert(value!=markOopDesc::no_hash,"invariant");TEVENT(hashCode:GENERATE);returnvalue;}从源码中可以发现生成策略是由一个hashCode全局变量控制的,默认为5;而这个变量定义在另一个头文件中:product(intx,hashCode,5,"(Unstable)selecthashCodegenerationalgorithm")源码写的很清楚...(unstable)selecthashCode生成的算法,和这里的定义可以通过jvm定义启动参数来控制,首先确认默认值:java-XX:+PrintFlagsFinal-version|grephashCodeintxhashCode=5{product}openjdkversion"1.8.0_282"OpenJDKRuntimeEnvironment(AdoptOpenJDK)(build1.8.0_282-b08)OpenJDK64-BitServerVM(AdoptOpenJDK)(build25.282-b08,mixedmode)所以我们可以通过jvm启动参数配置不同的hashcode生成算法,测试不同算法下的生成结果:-XX:hashCode=N现在来看看,每种hashcode生成算法的不同表现0型算法if(hashCode==0){//这种形式使用了一个不受保护的全局Park-MillerRNG,//因此两个线程有??可能竞争并生成相同的RNG。//在MP系统上,我们将对全局进行大量RW访问,因此//机制会导致大量一致性流量。价值=操作系统::随机();}本次生成算法采用随机数的Park-MillerRNGBuild策略。但是需要注意的是,这个随机算法在高并发的时候会自旋等待。第一个算法if(hashCode==1){//这种变体具有在STW操作之间稳定(幂等)的特性。这在某些1-0//同步方案中很有用。intptr_taddrBits=intptr_t(obj)>>3;value=addrBits^(addrBits>>5)^GVars.stwRandom;}这个算法,真的是获取对象的内存地址,直接获取对象的intptr_t类型指针。另外,Java系列面试题和答案都整理好了。微信搜索Java技术栈,后台发:面试,网上可以看。第二种算法if(hashCode==2){value=1;//forsensitivitytesting}不用解释了。。。一直返回1,应该用于内部测试场景。有兴趣的同学可以尝试-XX:hashCode=2开启这个算法,看看hashCode结果是不是都变成了1。第三种算法if(hashCode==3){value=++GVars.hcSequence;}这个算法也很简单,自增,所有对象的hashCode都使用这个自增变量。下面试试效果:System.out.println(newObject());System.out.println(newObject());System.out.println(newObject());System.out.println(newObject());System.out.println(newObject());System.out.println(newObject());//输出java.lang.Object@144java.lang.Object@145java.lang.Object@146java.lang.Object@147java.lang.Object@148java.lang.Object@149确实是自增的。。。有点意思。第四种算法if(hashCode==4){value=intptr_t(obj);}这里和第一种算法其实区别不大,都是返回对象地址,只是第一种算法是变种。第五种算法的最后一个也是默认的生成算法,当hashCode配置不等于0/1/2/3/4时使用:else{//Marsaglia'sxor-shiftschemewiththread-specificstate//这可能是最好的整体实现——我们//可能会在未来的版本中将其设为默认值。unsignedt=Self->_hashStateX;t^=(t<<11);自我->_hashStateX=自我->_hashStateY;->_hashStateY=Self->_hashStateZ;自我->_hashStateZ=自我->_hashStateW;unsignedv=Self->_hashStateW;v=(v^(v>>19))^(t^(t>>8));self->_hashStateW=v;价值=v;}这里是对当前状态值进行异或运算得到的一个hash值,比之前的自增算法和随机算法效率更高,但是重复率应该也会相对增加,但是hashCode重复有什么关系...原来jvm不保证这个值不会重复。比如HashMap中的链地址方式就是用来解决hash冲突的。近期热点文章推荐:1.1000+Java面试题及答案(2021最新版)2.别再满脑子if/else了,试试策略模式,真香!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!