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

          95% 的程序員都會犯的一個錯誤,Java 默認(rèn) hashCode 對象返回的并不是內(nèi)存地址!

          共 5620字,需瀏覽 12分鐘

           ·

          2021-11-14 00:21

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

          編輯:業(yè)余草

          segmentfault.com/a/1190000040152594

          推薦:https://www.xttblog.com/?p=5290

          ?

          先點(diǎn)贊再看,養(yǎng)成好習(xí)慣

          ?

          95% 的程序員都會犯的一個錯誤,甚至還包含某些知名講師。Java 中 Object 默認(rèn) hashCode 對象返回的并不是內(nèi)存地址!

          先看一個最簡單的打印。

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

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

          java.lang.Object@6659c656

          @符號后面的是什么?是 hashcode 還是對象的內(nèi)存地址?還是其他的什么值?

          其實(shí)@后面的只是對象的 hashcode 值,16進(jìn)制展示的 hashcode 而已,來驗(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);
          //?這個方法,也是獲取對象的 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 的核心方法:https://github.com/openjdk/jdk/blob/7ba83041b1d65545833655293d0976dfd1ffdea8/hotspot/src/share/vm/runtime/synchronizer.cpp#L557

          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 的全局變量控制的,默認(rèn)為5;而這個變量的定義在另一個頭文件里:https://github.com/openjdk/jdk/blob/7ba83041b1d65545833655293d0976dfd1ffdea8/hotspot/src/share/vm/runtime/globals.hpp#L1069

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

          源碼里很清楚了……「(非穩(wěn)定)選擇 hashCode 生成的算法」,而且這里的定義,是可以由 jvm 啟動參數(shù)來控制的,先來確認(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)

          「所以我們可以通過 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」的隨機(jī)數(shù)生成策略。不過需要注意的是……這個隨機(jī)算法在高并發(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)部的測試場景。

          有興趣的同學(xué),可以試試-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

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

          第 4 種算法

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

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

          第 5 種算法

          最后一種,「也是默認(rèn)的生成算法」,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?;
          ??}

          這里是通過當(dāng)前狀態(tài)值進(jìn)行異或(XOR)運(yùn)算得到的一個 hash 值,相比前面的自增算法和隨機(jī)算法來說效率更高,但重復(fù)率應(yīng)該也會相對增高,不過 hashCode 重復(fù)又有什么關(guān)系呢……

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

          總結(jié)

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

          瀏覽 69
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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久久人澡人人添人人爽欧美 | 麻豆视频在线观看 | 在线黄片视频 | 色婷婷aⅴ | 操逼操逼操逼操逼操逼操逼操逼操 |