First, let’s recap, there are 2 kinds of objects in .NET: value types and reference types that are created on the stack and in the heap (managed by GC), respectively. Value types are intended for storing plain data, like integers or characters. Every field in a value type object is being copied during the variable assignment. Also, the life-cycle of such objects depends on the usage scope. Default sizes of value types are defined in Common Type System:
Reference types, on the other hand, are references to a memory location spanned by an object instance in heap.
The following diagram shows the internal structure of CLR objects:
For reference type variables, a fixed size value (4 bytes, DWORD type) containing the address of an object instance created in heap (there are also Large Object Heap, HighFrequencyHeap, etc., but I won’t focus on this subject here) is pushed to stack. For example, in C++, this value is called a pointer, in .NET – a reference to the object.
The initial value of SyncBlock is null. However, SyncBlock may store the hash code of an object (when calling the GetHashCode method) as well, or the syncblk record, which is placed by runtime into object header during synchronization (using lock, or Monitor.Enter directly.).
Each type has its own MethodTable, and all instances of the same type use the same MethodTable. This table stores information about the type itself (interface, abstract class, etc).
Reference type pointer is a reference to object stored in a variable at offset +4 offset. The rest are class fields.