美团面试题:hashCode 和对象的内存地址有什么关系?

沙海 2021年8月2日04:34:19Java评论34字数 5392阅读17分58秒阅读模式
摘要

智能摘要

智能摘要文章源自JAVA秀-https://www.javaxiu.com/40011.html

hashCode=2来开启这个算法,看看hashCode结果是不是都变成1了。这个算法也很简单,所有对象的hashCode都使用这一个自增变量。文章源自JAVA秀-https://www.javaxiu.com/40011.html

原文约 1905 | 图片 3 | 建议阅读 4 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/40011.html

美团面试题:hashCode 和对象的内存地址有什么关系?

小哈学Java 文章源自JAVA秀-https://www.javaxiu.com/40011.html

美团面试题:hashCode 和对象的内存地址有什么关系?

来源 | juejin.cn/post/6971946031764209678

文章源自JAVA秀-https://www.javaxiu.com/40011.html

先看一个最简单的打印文章源自JAVA秀-https://www.javaxiu.com/40011.html

System.out.println(new Object());

会输出该类的全限定类名和一串字符串:文章源自JAVA秀-https://www.javaxiu.com/40011.html

java.lang.Object@6659c656

@符号后面的是什么?是 hashcode 还是对象的内存地址?还是其他的什么值?文章源自JAVA秀-https://www.javaxiu.com/40011.html

其实@后面的只是对象的 hashcode 值,16进制展示的 hashcode 而已,来验证一下:文章源自JAVA秀-https://www.javaxiu.com/40011.html

Object o = new Object();int hashcode = 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秀-https://www.javaxiu.com/40011.html

java.lang.Object@6659c6566659c65617171595101717159510

那对象的 hashcode 到底是怎么生成的呢?真的就是内存地址吗?文章源自JAVA秀-https://www.javaxiu.com/40011.html

本文内容基于 JAVA 8 HotSpot文章源自JAVA秀-https://www.javaxiu.com/40011.html

hashCode 的生成逻辑

JVM 里生成 hashCode 的逻辑并没有那么简单,它提供了好几种策略,每种策略的生成结果都不同。文章源自JAVA秀-https://www.javaxiu.com/40011.html

来看一下 openjdk 源码里生成 hashCode 的核心方法:文章源自JAVA秀-https://www.javaxiu.com/40011.html

static inline intptr_t get_next_hash(Thread * Self, oop obj) {  intptr_t value = 0 ;  if (hashCode == 0) {     // This form uses an unguarded global Park-Miller RNG,     // so it's possible for two threads to race and generate the same RNG.     // On MP system we'll have lots of RW access to a global, so the     // mechanism induces lots of coherency traffic.     value = os::random() ;  } else  if (hashCode == 1) {     // This variation has the property of being stable (idempotent)     // between STW operations.  This can be useful in some of the 1-0     // synchronization schemes.     intptr_t addrBits = 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 = 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 ;  }  value &= markOopDesc::hash_mask;  if (value == 0) value = 0xBAD ;  assert (value != markOopDesc::no_hash, "invariant") ;  TEVENT (hashCode: GENERATE) ;  return value;}

从源码里可以发现,生成策略是由一个 hashCode 的全局变量控制的,默认为5;而这个变量的定义在另一个头文件里:文章源自JAVA秀-https://www.javaxiu.com/40011.html

product(intx, hashCode, 5,                                                     "(Unstable) select hashCode generation algorithm" ) 

源码里很清楚了……(非稳定)选择 hashCode 生成的算法,而且这里的定义,是可以由 jvm 启动参数来控制的,先来确认下默认值:文章源自JAVA秀-https://www.javaxiu.com/40011.html

java -XX:+PrintFlagsFinal -version | grep hashCodeintx hashCode                                  = 5                                   {product}openjdk version "1.8.0_282"OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_282-b08)OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.282-b08, mixed mode)

所以我们可以通过 jvm 的启动参数来配置不同的 hashcode 生成算法,测试不同算法下的生成结果:文章源自JAVA秀-https://www.javaxiu.com/40011.html

-XX:hashCode=N

现在来看看,每种 hashcode 生成算法的不同表现。文章源自JAVA秀-https://www.javaxiu.com/40011.html

第 0 种算法

if (hashCode == 0) {     // This form uses an unguarded global Park-Miller RNG,     // so it's possible for two threads to race and generate the same RNG.     // On MP system we'll have lots of RW access to a global, so the     // mechanism induces lots of coherency traffic.     value = os::random();  }

这种生成算法,使用的一种Park-Miller RNG的随机数生成策略。不过需要注意的是……这个随机算法在高并发的时候会出现自旋等待文章源自JAVA秀-https://www.javaxiu.com/40011.html

第 1 种算法

if (hashCode == 1) {    // This variation has the property of being stable (idempotent)    // between STW operations.  This can be useful in some of the 1-0    // synchronization schemes.    intptr_t addrBits = intptr_t(obj) >> 3 ;    value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;}

这个算法,真的是对象的内存地址了,直接获取对象的 intptr_t 类型指针文章源自JAVA秀-https://www.javaxiu.com/40011.html

第 2 种算法

if (hashCode == 2) {    value = 1 ;            // for sensitivity testing}

这个就不用解释了……固定返回 1,应该是用于内部的测试场景。文章源自JAVA秀-https://www.javaxiu.com/40011.html

有兴趣的同学,可以试试-XX:hashCode=2来开启这个算法,看看 hashCode 结果是不是都变成 1 了。文章源自JAVA秀-https://www.javaxiu.com/40011.html

第 3 种算法

if (hashCode == 3) {    value = ++GVars.hcSequence ;}

这个算法也很简单,自增嘛,所有对象的 hashCode 都使用这一个自增变量。来试试效果:文章源自JAVA秀-https://www.javaxiu.com/40011.html

System.out.println(new Object());System.out.println(new Object());System.out.println(new Object());System.out.println(new Object());System.out.println(new Object());System.out.println(new Object());//outputjava.lang.Object@144java.lang.Object@145java.lang.Object@146java.lang.Object@147java.lang.Object@148java.lang.Object@149

果然是自增的……有点意思文章源自JAVA秀-https://www.javaxiu.com/40011.html

第 4 种算法

if (hashCode == 4) {    value = intptr_t(obj) ;}

这里和第 1 种算法其实区别不大,都是返回对象地址,只是第 1 种算法是一个变体。文章源自JAVA秀-https://www.javaxiu.com/40011.html

第 5 种算法

最后一种,也是默认的生成算法,hashCode 配置不等于 0/1/2/3/4 时使用该算法:文章源自JAVA秀-https://www.javaxiu.com/40011.html

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 ;  }

这里是通过当前状态值进行异或(XOR)运算得到的一个 hash 值,相比前面的自增算法和随机算法来说效率更高,但重复率应该也会相对增高,不过 hashCode 重复又有什么关系呢……文章源自JAVA秀-https://www.javaxiu.com/40011.html

本来 jvm 就不保证这个值一定不重复,像 HashMap 里的链地址法就是解决 hash 冲突用的.文章源自JAVA秀-https://www.javaxiu.com/40011.html

1. Spring Data JPA 与 MyBatis 对比,你喜欢用哪个?2. 从RPC预热转发看服务端性能调优3. 你的登录接口真的安全吗?快看看你有没有中招!4. ActiveMQ在分布式项目中的实际应用最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。文章源自JAVA秀-https://www.javaxiu.com/40011.html

谢谢支持哟 (*^__^*)文章源自JAVA秀-https://www.javaxiu.com/40011.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定