<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          美團(tuán)面試題:hashCode 和對(duì)象的內(nèi)存地址有什么關(guān)系?

          共 9928字,需瀏覽 20分鐘

           ·

          2021-08-05 09:35

          程序員的成長(zhǎng)之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享 
          關(guān)注


          閱讀本文大概需要 4 分鐘。

          來(lái)自: juejin.cn/post/6971946031764209678

          先看一個(gè)最簡(jiǎn)單的打印

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

          會(huì)輸出該類的全限定類名和一串字符串:

          java.lang.Object@6659c656

          @符號(hào)后面的是什么?是 hashcode 還是對(duì)象的內(nèi)存地址?還是其他的什么值?
          其實(shí)@后面的只是對(duì)象的 hashcode 值,16進(jìn)制展示的 hashcode 而已,來(lái)驗(yàn)證一下:

          Object o = new Object();
          int hashcode = o.hashCode();
          // toString
          System.out.println(o);
          // hashcode 十六進(jìn)制
          System.out.println(Integer.toHexString(hashcode));
          // hashcode
          System.out.println(hashcode);
          // 這個(gè)方法,也是獲取對(duì)象的 hashcode;不過(guò)和 Object.hashcode 不同的是,該方法會(huì)無(wú)視重寫的hashcode
          System.out.println(System.identityHashCode(o));

          輸出結(jié)果:

          java.lang.Object@6659c656
          6659c656
          1717159510
          1717159510

          那對(duì)象的 hashcode 到底是怎么生成的呢?真的就是內(nèi)存地址嗎?
          本文內(nèi)容基于 JAVA 8 HotSpot

          hashCode 的生成邏輯

          JVM 里生成 hashCode 的邏輯并沒(méi)有那么簡(jiǎn)單,它提供了好幾種策略,每種策略的生成結(jié)果都不同。
          來(lái)看一下 openjdk 源碼里生成 hashCode 的核心方法:

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

          從源碼里可以發(fā)現(xiàn),生成策略是由一個(gè) hashCode 的全局變量控制的,默認(rèn)為5;而這個(gè)變量的定義在另一個(gè)頭文件里:

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

          源碼里很清楚了……(非穩(wěn)定)選擇 hashCode 生成的算法,而且這里的定義,是可以由 jvm 啟動(dòng)參數(shù)來(lái)控制的,先來(lái)確認(rèn)下默認(rèn)值:

          java -XX:+PrintFlagsFinal -version | grep hashCode

          intx 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)

          所以我們可以通過(guò) jvm 的啟動(dòng)參數(shù)來(lái)配置不同的 hashcode 生成算法,測(cè)試不同算法下的生成結(jié)果:


          -XX:hashCode=N

          現(xiàn)在來(lái)看看,每種 hashcode 生成算法的不同表現(xiàn)。

          第 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的隨機(jī)數(shù)生成策略。不過(guò)需要注意的是……這個(gè)隨機(jī)算法在高并發(fā)的時(shí)候會(huì)出現(xiàn)自旋等待

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

          這個(gè)算法,真的是對(duì)象的內(nèi)存地址了,直接獲取對(duì)象的 intptr_t 類型指針

          第 2 種算法

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

          這個(gè)就不用解釋了……固定返回 1,應(yīng)該是用于內(nèi)部的測(cè)試場(chǎng)景。
          有興趣的同學(xué),可以試試-XX:hashCode=2來(lái)開(kāi)啟這個(gè)算法,看看 hashCode 結(jié)果是不是都變成 1 了。

          第 3 種算法

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

          這個(gè)算法也很簡(jiǎn)單,自增嘛,所有對(duì)象的 hashCode 都使用這一個(gè)自增變量。來(lái)試試效果:

          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());

          //output
          java.lang.Object@144
          java.lang.Object@145
          java.lang.Object@146
          java.lang.Object@147
          java.lang.Object@148
          java.lang.Object@149

          果然是自增的……有點(diǎn)意思

          第 4 種算法

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

          這里和第 1 種算法其實(shí)區(qū)別不大,都是返回對(duì)象地址,只是第 1 種算法是一個(gè)變體。

          第 5 種算法

          最后一種,也是默認(rèn)的生成算法,hashCode 配置不等于 0/1/2/3/4 時(shí)使用該算法:

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

          這里是通過(guò)當(dāng)前狀態(tài)值進(jìn)行異或(XOR)運(yùn)算得到的一個(gè) hash 值,相比前面的自增算法和隨機(jī)算法來(lái)說(shuō)效率更高,但重復(fù)率應(yīng)該也會(huì)相對(duì)增高,不過(guò) hashCode 重復(fù)又有什么關(guān)系呢……
          本來(lái) jvm 就不保證這個(gè)值一定不重復(fù),像 HashMap 里的鏈地址法就是解決 hash 沖突用的.
          <END>

          推薦閱讀:

          B 站焊武帝爆火出圈:純手工拼晶體管自制 CPU,耗時(shí)半年,可跑程序

          40 個(gè) SpringBoot 常用注解

          最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)個(gè)「在看」,點(diǎn)擊上方小卡片,進(jìn)入公眾號(hào)后回復(fù)「面試題」領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          朕已閱 

          瀏覽 67
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产三级日本三级在线播放 | 四虎性爱网站 | 搭讪人妻中文字幕 | 久久色国产精品 | 豆花综合视频在线观看 |