揭示同步块索引(下):总结2011-10-15 博客园 横刀天笑前面,我用两篇文章详细的讨论了同步块索引在lock和GetHashCode所起的作用。不过两篇文章是分开 来讨论的。那可能有人会问,如果我有一个object,它既作为lock的lockHelper对象,也要调用它的 GetHashCode方法该怎么办,难道这个同步块索引还可以承担这两个任务么。同步块索引是可以承担这两 个任务,但是里面却隐藏着更大的秘密,我们先来看看与同步块索引相关的结构:

大致就是这样的一个结构,一个对象的ObjectHeader中的SyncBlockIndex指向一个Sync Block Entry Table中的一项,这里用虚线表示,是说明这里不是使用指针直接的指向,而是一个索引,这样有个什么 好处呢,就是CLR可以随便把这个Table放在哪里,也可以按需增大这个Table的容量,反正我这里使用的 是索引而不是指针,是间接的指向。这个Table里的每一项都是一个SyncTableEntry,这个 SyncTableEntry有两个字段,第一个字段是一个SyncBlock的指针,指向一个SyncBlock对象。还有一个字 段是一个Object指针,有了这个指针CLR就可以跟踪这个SyncBlock是哪个对象的,而且SyncTableEntry和 SyncBlock不是放在GC管理的内存中,所以可以根据这个Object*来跟踪对应的对象的实例,当对象死亡后 ,可以回收对应的SyncBlock和SyncTableEntry,不过这个Object的指针是一个弱引用(弱引用的作用是 ,如果没有任何强引用引用该对象,则该对象可以被认为是垃圾,允许被垃圾收集)。不过要注意的是,上面这种结构并不是一个对象“与生俱来”的,也就是说对象刚初始化的时候并不 如此。当一个对象刚初始化的时候,在ObjectHeader中,SyncBlockIndex字段是为0的,这个在上一篇文 章中的Visual Studio + SOS的实验中我们已经见到过。而如果调用对象的GetHashCode方法,则对象的 ObjectHeader中的SyncBlockIndex字段的低26位则用来存储该对象的HashCode,而高6位作为一个标识, 表示现在SyncBlockIndex作为存储HashCode之用,具体做法就是将这个SyncBlockIndex与 BIT_SBLK_IS_HASHCODE (#define BIT_SBLK_IS_HASHCODE 0x04000000)作或运算,判别的时候作一下与运 算。这个在上一篇文章中也介绍了。而如果调用对象的GetHashCode方法之后,继续将该对象作为lock的 对象使用呢?这个时候SyncBlockIndex的低26位会摇身一变,变成一个索引,而且还与 BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX (#define BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX 0x08000000)作一下 或运算,表示这个SyncBlockIndex现在啊既有存储HashCode之功用,又要作为lock的对象。那既然这低26位变成了索引,那原来的HashCode跑到哪里去了呢?这个就要一探SyncBlock的结构了: