<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      揭示同步塊索引(中):如何獲得對象的HashCode

      2009-08-13 22:44  橫刀天笑  閱讀(6775)  評論(12)    收藏  舉報

      題外話:為了嘗鮮,也興沖沖的安裝了Win7,不過興奮之余卻郁悶不已,由于是用Live Writer寫博客,寫了好幾篇草稿,都完成了80%左右,沒有備份全部沒了。欲哭無淚,只好重寫了。

      Visual Studio + SOS 小實驗

      咋一看標題,覺得有些奇怪,同步塊索引和HashCode有啥關系呢。從名字上來看離著十萬八千里。在不知道細節之前,我也是這樣想的,知道細節之后,才發現這兩兄弟如此親密。我們還是先來用Visual Studio + SOS,看一個東西,下面是作為小白兔的示例代碼:

         1: using System;
         2: public class Program
         3: {
         4:     static void Main()
         5:     {
         6:         Foo f = new Foo();
         7:         Console.WriteLine(f.GetHashCode());
         8:  
         9:         Console.ReadLine();
        10:     }
        11: }
        12: //就這么一個簡單的類
        13: public class Foo
        14: {
        15:  
        16: }

      (使用Visual Studio + SOS調試的時候,請先在項目的屬性,調試欄里設置“允許非托管代碼調試”)

      我們分別在第7行,第9行設置斷點,F5運行,當程序停在第一個斷點處時(此時f.GetHashCode()還沒有執行),我們在Visual Studio的立即窗口里輸入:

         1: .load sos.dll
         2: extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded
         3: !dso
         4: PDB symbol for mscorwks.dll not loaded
         5: OS Thread Id: 0x1730 (5936)
         6: ESP/REG  Object   Name
         7: 0013ed78 01b72d58 Foo
         8: 0013ed7c 01b72d58 Foo
         9: 0013efc0 01b72d58 Foo
        10: 0013efc4 01b72d58 Foo

      使用.load sos.dll加載sos模塊,然后使用!dso,我們找到了Foo類型的f對象的內存地址:01b72d58,然后使用Visual Studio調試菜單下的查看內存的窗口,查看f對象頭部的內容:

      image

      陰影遮住的00 00 00 00就是同步塊索引所在的地方了,可以看得出來,此時同步塊索引的值還是0(后面會對這個做解釋),然后繼續F5,程序運行到下一個斷點處,這個時候f.GetHashCode()也已調用了,細心的你就會發現,原來對象同步塊索引所在的地方的值變了:

      image

      Visual Studio這個內存查看器有個很好的功能,對內存變化的以紅色標出。我們看到,原來是00 00 00 00變成了現在的4a 73 78 0f。嗯,看來HashCode的獲取和同步塊索引還是有一些關系的,不然調用GetHashCode方法為什么同步塊索引的值會變化呢。再來看看Console.WriteLine(f.GetHashCode())的輸出:

      image 
      不知道著兩個值有沒有什么關系,我們先把它們都換算成二進制吧。注意,這里的4a 73 78 0f是低位在左,高位在右,下面的十進制是高位再左,低位在右,那4a 73 78 0f實際上就是0x0f78734a了。

      0x0f78734a:00001111011110000111001101001010

         58225482:00000011011110000111001101001010

       Rotor源代碼

      我們先用0補齊32位,突然發現這兩者低26位居然是一模一樣的(紅色標出的部分),這是巧合還是必然?為了一探究竟只好搬出Rotor的源代碼,從源代碼里看看是否能發現什么東西。還是遵循老路子,我們先從托管代碼開始:

         1: public virtual int GetHashCode()
         2: {
         3:    return InternalGetHashCode(this);
         4: }
         5: [MethodImpl(MethodImplOptions.InternalCall)]
         6: internal static extern int InternalGetHashCode(object obj);

      在本系列的第一篇文章已經提到過,標記有[MethodImpl(MethodImplOptions.InternalCall)]特性的方法是使用Native Code的方式實現的,在Rotor中,這些代碼位于sscli20\clr\src\vm\ecall.cpp文件中:

         1: FCFuncElement("InternalGetHashCode", ObjectNative::GetHashCode)
         2: FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) {
         3:     DWORD idx = 0;
         4:     OBJECTREF objRef(obj);
         5:     idx = GetHashCodeEx(OBJECTREFToObject(objRef));
         6:     return idx;
         7: }
         8: FCIMPLEND
         9: INT32 ObjectNative::GetHashCodeEx(Object *objRef)
        10: {
        11:     // This loop exists because we're inspecting the header dword of the object
        12:     // and it may change under us because of races with other threads.
        13:     // On top of that, it may have the spin lock bit set, in which case we're
        14:     // not supposed to change it.
        15:     // In all of these case, we need to retry the operation.
        16:     DWORD iter = 0;
        17:     while (true)
        18:     {
        19:         DWORD bits = objRef->GetHeader()->GetBits();
        20:  
        21:         if (bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX)
        22:         {
        23:             if (bits & BIT_SBLK_IS_HASHCODE)
        24:             {
        25:                 // Common case: the object already has a hash code
        26:                 return  bits & MASK_HASHCODE;
        27:             }
        28:             else
        29:             {
        30:                 // We have a sync block index. This means if we already have a hash code,
        31:                 // it is in the sync block, otherwise we generate a new one and store it there
        32:                 SyncBlock *psb = objRef->GetSyncBlock();
        33:                 DWORD hashCode = psb->GetHashCode();
        34:                 if (hashCode != 0)
        35:                     return  hashCode;
        36:  
        37:                 hashCode = Object::ComputeHashCode();
        38:  
        39:                 return psb->SetHashCode(hashCode);
        40:             }
        41:         }
        42:         else
        43:         {
        44:             // If a thread is holding the thin lock or an appdomain index is set, we need a syncblock
        45:             if ((bits & (SBLK_MASK_LOCK_THREADID | (SBLK_MASK_APPDOMAININDEX << SBLK_APPDOMAIN_SHIFT))) != 0)
        46:             {
        47:                 objRef->GetSyncBlock();
        48:                 // No need to replicate the above code dealing with sync blocks
        49:                 // here - in the next iteration of the loop, we'll realize
        50:                 // we have a syncblock, and we'll do the right thing.
        51:             }
        52:             else
        53:             {
        54:                 DWORD hashCode = Object::ComputeHashCode();
        55:  
        56:                 DWORD newBits = bits | BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE | hashCode;
        57:  
        58:                 if (objRef->GetHeader()->SetBits(newBits, bits) == bits)
        59:                     return hashCode;
        60:                 // Header changed under us - let's restart this whole thing.
        61:             }
        62:         }
        63:     }
        64: }

      代碼很多,不過大部分操作都是在做與、或、移位等。而操作的對象就是這行代碼獲取的:objRef->GetHeader()->GetBits(),實際上就是獲取同步塊索引。

      想想,在第一個斷點命中的時候,同步塊索引的值還是0x00000000,那應該是下面這塊代碼執行:

         1: DWORD hashCode = Object::ComputeHashCode();
         2: DWORD newBits = bits | BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE | hashCode;
         3: if (objRef->GetHeader()->SetBits(newBits, bits) == bits)
         4:     return hashCode;

      通過Object的ComputeHashCode方法算出一個哈希值來(由于本文不是關注哈希算法的,所以這里不討論這個ComputeHashCode方法的實現)。然后進行幾個或操作(這里還要與原先的bits或操作是為了保留原來的值,說明這個同步塊索引還起了別的作用,比如上篇文章的lock),然后將同步塊索引中老的位換掉。從這里我們還看不出來什么。不過,如果我們再次對這個對象調用GetHashCode()方法呢?那同步塊索引不再為0x00000000,而是0x0f78734a,在來看看幾個定義的常量的值:

         1: #define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX    0x08000000
         2: #define BIT_SBLK_IS_HASHCODE            0x04000000
         3: #define HASHCODE_BITS                   26
         4: #define MASK_HASHCODE                   ((1<<HASHCODE_BITS)-1)

      從剛才設置hashcode的地方可以看到:DWORD newBits = bits | BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE | hashCode;

      所以開頭的兩個if都可以通過了,返回的hashcode就是bits & MASK_HASHCODE。

      這個MASK_HASHCODE是將1向左移26位=100000000000000000000000000,然后減1=00000011111111111111111111111111(低26位全部為1,高6位為0),然后與同步塊索引相與,其實這里的作用不就是為了取出同步塊索引的低26位的值么。再回想一下本文開頭的那個試驗,原來不是巧合啊。

      連上上一篇,我們可以看到同步塊索引不僅僅起到lock的作用,有時還承擔著存儲HashCode的責任。實際上同步塊索引是這樣的一個結構:總共32位,高6位作為控制位,后26的具體含義隨著高6位的不同而變化,高6位就像很多小開關,有的打開(1),有的關閉(0),不同位的打開和關閉有著不同的意義,程序也就知道低26位到底是干啥的了。這里的設計真是巧妙,不斷占用內存很緊湊,程序也可以靈活處理,靈活擴展。

       

      后記

      本篇和上一篇一樣,都是單獨將獨立的內容拿出來,這樣可以更簡單的來闡述。比如在本文中,我只設想同步塊索引做hashcode的存儲,這個時候,同步塊索引就干干凈凈(本文前面的試驗中先得到的同步塊索引就是一個0),但實際中同步塊索引可能擔任更多的職責,比如既lock,又要獲取HashCode,這個時候情況就更復雜,這個在后面一篇文章會綜合各種情況更詳細的說明。

      主站蜘蛛池模板: 国产在线精品中文字幕| 一二三四中文字幕日韩乱码| 精品日韩亚洲av无码| 艳妇乳肉豪妇荡乳xxx| 久久不见久久见免费影院www日本| 99久久无色码中文字幕| av中文字幕在线二区| 久9re热视频这里只有精品免费| 高清性欧美暴力猛交| 亚洲男人av天堂久久资源| 97精品国产91久久久久久久| av永久免费网站在线观看| 亚洲av午夜福利大精品| 久久精品亚洲日本波多野结衣| 色色97| 亚欧洲乱码视频在线专区| 国产一区在线播放av| 蜜桃av色偷偷av老熟女| 日韩有码中文在线观看| 亚洲人成网网址在线看| 国产精品午夜福利视频234区 | 亚洲AV片一区二区三区| 国产精品欧美福利久久| 婷婷综合缴情亚洲| 欧美亚洲综合成人A∨在线| 济南市| 国产微拍一区二区三区四区| 国产精品久久福利新婚之夜| 同性男男黄gay片免费| 福利视频一区二区在线| 大又大又粗又硬又爽少妇毛片| 卡一卡二卡三精品| 无码AV中文字幕久久专区| 国产黄色av一区二区三区| 精品国产成人A区在线观看| 视频一区二区不中文字幕| 国产成人精品无人区一区| 国产免费AV片在线看| 国产女人喷潮视频免费| 欧美日韩国产综合草草| 蜜桃网址|