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

          hashCode 的值是怎么生成的?對象內(nèi)存地址嗎?

          共 9856字,需瀏覽 20分鐘

           ·

          2021-06-25 05:18

          作者:空無

          來源:SegmentFault 思否社區(qū)


          先看一個最簡單的打印
          System.out.println(new Object());
          會輸出該類的全限定類名和一串字符串:
          java.lang.Object@6659c656
          @符號后面的是什么?是 hashcode 還是對象的內(nèi)存地址?還是其他的什么值?
          其實@后面的只是對象的 hashcode 值,16進制展示的 hashcode 而已,來驗證一下:
          Object o = new Object();
          int hashcode = o.hashCode();
          // toString
          System.out.println(o);
          // hashcode 十六進制
          System.out.println(Integer.toHexString(hashcode));
          // hashcode
          System.out.println(hashcode);
          // 這個方法,也是獲取對象的 hashcode;不過和 Object.hashcode 不同的是,該方法會無視重寫的hashcode
          System.out.println(System.identityHashCode(o));
          輸出結(jié)果:
          java.lang.Object@6659c656
          6659c656
          1717159510
          1717159510
          那對象的 hashcode 到底是怎么生成的呢?真的就是內(nèi)存地址嗎?
          本文內(nèi)容基于 JAVA 8 HotSpot

          hashCode 的生成邏輯

          JVM 里生成 hashCode 的邏輯并沒有那么簡單,它提供了好幾種策略,每種策略的生成結(jié)果都不同。
          來看一下 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),生成策略是由一個 hashCode 的全局變量控制的,默認為5;而這個變量的定義在另一個頭文件里:
           product(intx, hashCode, 5,                                            
                   "(Unstable) select hashCode generation algorithm" ) 
          源碼里很清楚了……(非穩(wěn)定)選擇 hashCode 生成的算法,而且這里的定義,是可以由 jvm 啟動參數(shù)來控制的,先來確認下默認值:
          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)
          所以我們可以通過 jvm 的啟動參數(shù)來配置不同的 hashcode 生成算法,測試不同算法下的生成結(jié)果:
          -XX:hashCode=N
          現(xiàn)在來看看,每種 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的隨機數(shù)生成策略。不過需要注意的是……這個隨機算法在高并發(fā)的時候會出現(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 ;
          }
          這個算法,真的是對象的內(nèi)存地址了,直接獲取對象的 intptr_t 類型指針

          第 2 種算法

          if (hashCode == 2) {
              value = 1 ;            // for sensitivity testing
          }
          這個就不用解釋了……固定返回 1,應(yīng)該是用于內(nèi)部的測試場景。

          有興趣的同學,可以試試-XX:hashCode=2來開啟這個算法,看看 hashCode 結(jié)果是不是都變成 1 了。

          第 3 種算法

          if (hashCode == 3) {
              value = ++GVars.hcSequence ;
          }
          這個算法也很簡單,自增嘛,所有對象的 hashCode 都使用這一個自增變量。來試試效果:
          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
          果然是自增的……有點意思

          第 4 種算法

          if (hashCode == 4) {
              value = intptr_t(obj) ;
          }
          這里和第 1 種算法其實區(qū)別不大,都是返回對象地址,只是第 1 種算法是一個變體。

          第 5 種算法

          最后一種,也是默認的生成算法,hashCode 配置不等于 0/1/2/3/4 時使用該算法:
          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 ;
            }
          這里是通過當前狀態(tài)值進行異或(XOR)運算得到的一個 hash 值,相比前面的自增算法和隨機算法來說效率更高,但重復(fù)率應(yīng)該也會相對增高,不過 hashCode 重復(fù)又有什么關(guān)系呢……

          本來 jvm 就不保證這個值一定不重復(fù),像 HashMap 里的鏈地址法就是解決 hash 沖突用的

          總結(jié)

          hashCode 可以是內(nèi)存地址,也可以不是內(nèi)存地址,甚至可以是 1 這個常數(shù)或者自增數(shù)!想用什么算法,它都可以!


          點擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動和交流,掃描下方”二維碼“或在“公眾號后臺回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  日本在线黄 | 日日干免费视频 | 成人亚洲天堂 | 在线永久 | 黄色大片免费看 |