<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)系?

          共 9756字,需瀏覽 20分鐘

           ·

          2021-08-16 02:21

          點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”

          回復(fù)”學(xué)習(xí)資料“獲取學(xué)習(xí)寶典

          來(lái)源 | juejin.cn/post/6971946031764209678


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

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

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

          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ú)視重寫(xiě)的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 類(lèi)型指針

          第 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 沖突用的.

          后臺(tái)回復(fù) 學(xué)習(xí)資料 領(lǐng)取學(xué)習(xí)視頻


          如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

          瀏覽 48
          點(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>
                  操逼动漫视频 | 午夜一区二区三区免费视频 | 91精品国产综合久久久不打电影 | 奇米网狠狠干 | 在线观看麻豆免费视频 |