揭示同步塊索引(下):總結(jié)
2009-08-18 15:57 橫刀天笑 閱讀(3921) 評論(7) 收藏 舉報前面,我用兩篇文章詳細(xì)的討論了同步塊索引在lock和GetHashCode所起的作用。不過兩篇文章是分開來討論的。那可能有人會問,如果我有一個object,它既作為lock的lockHelper對象,也要調(diào)用它的GetHashCode方法該怎么辦,難道這個同步塊索引還可以承擔(dān)這兩個任務(wù)么。同步塊索引是可以承擔(dān)這兩個任務(wù),但是里面卻隱藏著更大的秘密,我們先來看看與同步塊索引相關(guān)的結(jié)構(gòu):
大致就是這樣的一個結(jié)構(gòu),一個對象的ObjectHeader中的SyncBlockIndex指向一個Sync Block Entry Table中的一項,這里用虛線表示,是說明這里不是使用指針直接的指向,而是一個索引,這樣有個什么好處呢,就是CLR可以隨便把這個Table放在哪里,也可以按需增大這個Table的容量,反正我這里使用的是索引而不是指針,是間接的指向。這個Table里的每一項都是一個SyncTableEntry,這個SyncTableEntry有兩個字段,第一個字段是一個SyncBlock的指針,指向一個SyncBlock對象。還有一個字段是一個Object指針,有了這個指針CLR就可以跟蹤這個SyncBlock是哪個對象的,而且SyncTableEntry和SyncBlock不是放在GC管理的內(nèi)存中,所以可以根據(jù)這個Object*來跟蹤對應(yīng)的對象的實例,當(dāng)對象死亡后,可以回收對應(yīng)的SyncBlock和SyncTableEntry,不過這個Object的指針是一個弱引用(弱引用的作用是,如果沒有任何強引用引用該對象,則該對象可以被認(rèn)為是垃圾,允許被垃圾收集)。
不過要注意的是,上面這種結(jié)構(gòu)并不是一個對象“與生俱來”的,也就是說對象剛初始化的時候并不如此。當(dāng)一個對象剛初始化的時候,在ObjectHeader中,SyncBlockIndex字段是為0的,這個在上一篇文章中的Visual Studio + SOS的實驗中我們已經(jīng)見到過。而如果調(diào)用對象的GetHashCode方法,則對象的ObjectHeader中的SyncBlockIndex字段的低26位則用來存儲該對象的HashCode,而高6位作為一個標(biāo)識,表示現(xiàn)在SyncBlockIndex作為存儲HashCode之用,具體做法就是將這個SyncBlockIndex與BIT_SBLK_IS_HASHCODE (#define BIT_SBLK_IS_HASHCODE 0x04000000)作或運算,判別的時候作一下與運算。這個在上一篇文章中也介紹了。而如果調(diào)用對象的GetHashCode方法之后,繼續(xù)將該對象作為lock的對象使用呢?這個時候SyncBlockIndex的低26位會搖身一變,變成一個索引,而且還與BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX (#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000)作一下或運算,表示這個SyncBlockIndex現(xiàn)在啊既有存儲HashCode之功用,又要作為lock的對象。
那既然這低26位變成了索引,那原來的HashCode跑到哪里去了呢?這個就要一探SyncBlock的結(jié)構(gòu)了:
我們看到最后一個字段,這個字段就是如果SyncBlockIndex還做其他用途是,CLR會將計算所得的hashcode放到這里。而如果對象只作lock對象使用,而沒有調(diào)用GetHashCode方法,則這個字段為0。根據(jù)調(diào)用的順序,這個m_dwHashCode的設(shè)置有兩種方式:
1、已經(jīng)調(diào)用了GetHashCode方法,然后作lock之用,那這里的m_dwHashCode就是之前存儲在SyncBlockIndex中的低26位。
2、先作lock之用,然后調(diào)用GetHashCode,那m_dwHashCode就是當(dāng)時新生成的HashCode,然后放在這里的。
從圖中我們還有ADIndex這么一個字段,這個字段是表示當(dāng)前這個對象屬于哪個AppDomain,實際上這個字段也可以在SyncBlockIndex里設(shè)置,但是如果SyncBlockIndex要擔(dān)負(fù)別的責(zé)任,比如該對象作為lock對象時,ADIndex就在SyncBlock里這個字段設(shè)置了。關(guān)于為什么需要這個ADIndex我現(xiàn)在還沒弄清楚,等我弄明白了,再來更新這篇文章。
SyncBlock的第一個字段是AwareLock,實際上這個東西和我第一篇文章中提到的CRITICAL_SECTION結(jié)構(gòu)是一樣的,具體細(xì)節(jié)可以參見“揭示同步索引塊(上)-從lock開始”這篇文章。
而這里的SLink字段有兩個作用:
1、當(dāng)SyncBlock是活動的時候,這個字段將作為一個隊列,保存在這里排隊的線程(作為lock對象時)。
2、當(dāng)SyncBlock被回收時,這個字段就作為空閑的SyncBlock列表。
好了,同步塊索引的相關(guān)用途和結(jié)構(gòu)在上、中、下這三篇文章中基本討論完了。沒想到這么一個小小的,不起眼的同步塊索引卻有這么一番作用。從這個同步塊索引的使用上,也可以看得出來微軟的CLR Team在設(shè)計的時候,對內(nèi)存、性能可謂斤斤計較,充分的利用每一個bit,一個bit的不同就會表示不同的作用。
浙公網(wǎng)安備 33010602011771號