Sync Block Index (SBI) \ Object Header Word




Introduction

When observing .Net Reference Type - Memory Layout, we'll notice that each reference type contains additional overhead – the following 2 fields:

           1.     Object Header Word (a.k.a Sync Block Index - SBI)
           2.     Type Object Pointer -TOP (a.k.a Method Table Pointer - MT)


Remark:
For additional detailed information, please review the .Net Type Internals article.

In '.Net Type Internals' article I elaborated on 'Reference Type' and 'Value Type' memory layout and extensively explored additional .Net Type Internals topics, such as: the Method Table internal CLR structure, with relevant examples.



Sync Bloc Index

The Sync Bloc Index (a.k.a Object Header Word) field is being used for several different purposes:

        1.     Thread Synchronization – using the CLR Monitor Mechanism with the lock keyword.
        2.     Storing the object's Hash Code – used in a hash-based collection, such as: Dictionary.
        3.     Garbage Collector algorithm – used in Mark phase.
        4.     Garbage Collector Finalization mechanism.


In addition to the data above, the CLR uses few bits in the Sync Block Index field, in order to determine which information of these options is currently stored in it.

In this post I'll use simple code example to demonstrate the changes in the Sync Block Index field, when implementing the first 2 options:
            -        Thread Synchronization using the lock keyword.
            -        Store the object's Hash Code when used in a hash-based collection.


Remark:
Additional detailed information could be found in the .Net Garbage Collection article I'll post shortly.

In this article I elaborated on the Garbage Collector's algorithm, different phases (Mark, Sweep & Compact), GC Roots, GC Handles, GC Generations, LOH, GC Modes, GC Finalization mechanism, the Dispose patterns, and more.




Example 1 – Thread Synchronization


1.     Create a simple class Person:

In order to present the SBI different values in some of the scenarios we'll examine, I wrote the following 'Person' class:

public class Person
{
    public int Id { get; set; }
}


Person instance memory layout

The following image describes a person instance layout in the Managed Heap. (In a 32bit system)

When we create an instance of an object, we are creating a reference to that object, which points to the beginning of the Type Object Pointer (a.k.a MT Pointer), whereas the Object Word Header (a.k.a Sync Block Index) is laid in a negative offset from that object.



   
2.     Create a method with thread synchronization:

Next, I'll create a method that uses the lock keyword for thread synchronization:

private static void SyncBlockIndexUsingLockExample()
{
    Person person = new Person {Id = 4};

    lock (person)
    {
        // Thread synchronization code...
    }
}


Remark: Normally, I wouldn't create a 'Person' object to lock a code of block; I'll create a simple object. But for this example, I'd like to present the Sync Block Index, and it would be much easier to present it using the 'Person' object.



3.     Debugging in Visual Studio:

I'll demonstrate this using the Visual Studio different debug windows:

-        Disassembly window – to show the assembly calls and the relevant registers.
-        Registers window – to retrieve the relevant memory addresses.
-        Memory window – to show the actual values stored in the managed heap memory.
-        Immediate window – to use the SOS DLL, to perform debugging and present the MT and other CLR data structures.


3.1  Disassembly window

When debugging the code prior to creating the person instance, we could see from the 'Disassembly window' that the compiler is using the ECX register to store the 'person' new instance location in the Managed Heap.





3.2  Registers window & Memory window

So I copied the ECX register value to the address textbox in the memory window:




After I copied the ECX register value to the 'Memory1 window', we could see our entire Person object.

We could see its:
           -        Sync Block index Field: currently its value is zero.
           -        Method Table Pointer address: 001a3424
           -        Id Filed: we set its value to 4.


Remark: MT Pointer & SBI addresses

As described above, we could see that the Person's address - 0x01C69424 points to the beginning of the object, meaning to the Method Table Pointer filed, and the Sync Block Index field reside in a negative offset from it. (-4 Bytes in a 32bit system, thus the SBI address is: 0x01C69420).

Next, we'll continue debugging and enter the locked block of code.





Now we could see that the value of the Sync Block Index filed was changed to: 00000001.


To conclude the example up to now:

We managed to demonstrate that when using the lock keyword for thread synchronization, the CLR uses the Sync Block Index field (of the locking object) and store relevant data for its own use; this is part of the CLR Monitor Mechanism.




CLR Monitor Mechanism

The lock keyword is actually a 'syntactic sugar' for the Monitor.Enter() and Monitor.Exit() methods that are the implementation of the CLR Monitor Mechanism, to control the thread synchronization.

Meaning, the compiler translate the lock keyword code to:

In .Net Framework 3.5 - mscorlib.dll, v2.0.0.0
private static void SyncBlockIndexUsingLockExample_3()
{
    Person person = new Person { Id = 4 };

    try
    {
         Monitor.Enter(person);

         // Thread synchronization code...
    }
    finally
    {
         Monitor.Exit(person);
    }
}

In .Net Framework 4.6.1 - mscorlib.dll, v4.0.0.0
private static void SyncBlockIndexUsingLockExample_2()
{
    Person person = new Person { Id = 4 };
    bool lockTaken = false;

    try
    {
        Monitor.Enter(person, ref lockTaken);

        // Thread synchronization code...
    }
    finally
    {
        if (lockTaken) Monitor.Exit(person);
    }
}


If we'll observe the MSIL code of our method, we could see the following:






3.3  Immediate window

In the immediate window I'll use the SOS.DLL extension to present all of the above (and more), by debugging our session and by examining the relevant Objects, Method Tables, the Sync Block Index and other relevant CLR data.

SOS.DLL – Son of Strike

The SOS DLL is a debugger extension, provided as part of the .Net Framework (%windir%\Microsoft.NET\Framework\v4.0.30319), and could be used to perform debugging on .Net processes, and to display relevant information on the CLR internals.

Normally, I would use it in the WinDbg tool (provided as part of the Debugging Tools for Windows tool set), but for this example I'll use the SOS.DLL inside Visual Studio in order to present the relevant information.

Remarks:
-        I'll add other posts to demonstrate how to use the SOS extension in visual studio and in WinDbg, for different examples with relevant commands.
-        There are additional debugger extensions that I'll probably describe in other debugging posts, such as: sosex.dll, psscor2.dll, psscor4.dll, ClrMD.



I've performed the following commands:

           1.      Load the SOS DLL:
-        .load sos.dll
-        Output snippet:

.load sos.dll

extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded


           2.      Review the SyncBlock
-        !SyncBlk
-        Output snippet:

!SyncBlk

Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
-----------------------------
Total           0
CCW             0
RCW             0
ComClassFactory 0
Free            0





Remark: Thin Lock

As we could observe in the output above the sync block table is empty, this is since the CLR optimizes the Monitor Mechanism, and uses a 'thin lock'

The Thin Lock mechanism was introduced in CLR 2.0, in order to improve performance.

Simply put, the CLR creates the Sync Block lazily, only if there are other threads that need to execute the same code block, otherwise, the CLR uses a thin lock to manage the synchronization.

In such cases the '!SyncBlk' command can’t present thin locks, instead we could use the '!DumpHeap –thinlock' command.


           3.      Find Thin Locks

-        !DumpHeap –thinlock
-        Output snippet:

!DumpHeap –thinlock

 Address       MT     Size
01c69424 001a3424       12      ThinLock owner 1 (004a03b8) Recursive 0


           4.      The object:

-        !DumpObj 01c69424
-        Output snippet:

!DumpObj 01c69424

Name: TestPerfExample.Person
MethodTable: 001a3424
EEClass: 001a191c
Size: 12(0xc) bytes

 (C:...\TestPerfExample\bin\Debug\TestPerfExample.exe)

Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
67042f74  4000001        4         System.Int32  1 instance        4 k__BackingField
ThinLock owner 1 (004a03b8), Recursive 0





---------------------------------------------------------------------------------------------------------------------------


Example 2 – Object's Hash Code


1.     Create a method with a Dictionary:

In this example, I'll create a method that uses a Dictionary (hash-based collection) to present the changes in the Sync Block Index, while using the 'Person' object as a key:

private static void SyncBlockIndexUsingDictionaryExample()
{
    Dictionary<Person, string> dictionary = new Dictionary<Person, string>();

    Person person = new Person() { Id = 4 };
    dictionary.Add(person, "Demonstrating Sync Block Index");
}



Debug the method in 2 phases:

1.1  Create the 'Person' object





       -        Disassembly window: We could see that the compiler uses the ECX Register to store the 'person' object in the Managed (GC) Heap memory.
       -        Registers window: I copied the ECX register value to the Memory 1 window.
       -        Memory 1 window: We could observe the 'person' object as it appears in the Managed Heap.
       -        Currently the Sync Block Index field is all zeros, since we hadn't added it to the Dictionary yet.



1.2  Add the 'Person' object to the dictionary




We could clearly see that after adding the 'person' to the dictionary, its Sync Block Field was modified accordingly!


Remark:
As already mentioned, since the SBI has multiple purposes and could store different values, the CLR adds the SBI additional bit(s) in order to distinguish which kind of information is currently stored in it.

Thus, the value appeared above is a combination of the GetHashCode() returned value with this bit(s).





Summary

In this post we explored the different purposes of the Sync Block Index field declared for every object we create.

In addition I demonstrated how the CLR uses the Sync Block Index for:
         1.      Thread synchronization.
         2.      Storing the object's hash code.




The End

Hope you enjoyed!
Appreciate your comments…

Yonatan Fedaeli



1 comment:

CHAVA32 said...

Greatest!!! I am looking for this al the day and finally i found it!!! One millon oh thanks for you!!!