.NET Internals Cookbook Part 6 — Object internals

This is the sixth part of the .NET Internals Cookbook series. For your convenience you can find other parts in the table of contents in Part 0 – Table of contents

36. What is a shim?

.NET applications are started directly, the same way as native applications are. The entry point of the binary points to mscoree.dll (execution engine) which then checks the metadata of the file, loads correct .NET runtime and passes the execution using _CorExeMain function. The part of mscoree is sometimes called “loading shim”. See this for more details.

This means that in native code you can load .NET manually and execute some managed code. See this to get some more code.

37. How many bytes does an empty structure have?

“Zero” makes perfect sense as the structure is empty so it should occupy no space. However, this would make pointer arithmetic a little weird — you could allocate two structures, one next to another and they would all be pointing to the same address. Because of that the empty structure takes one byte. See this code:


You could also ask how many bytes would empty class have — it is 3 pointers (so 12 bytes on x86 or 24 bytes on x64). This includes space for sync block, space for Method Table pointer, and some more bytes so the GC can work properly.

38. What is a sync block?

Information that the object is locked must be stored somewhere. Whenever you have a reference to an object, it does not point to the object beginning. Actually, before the place pointed by the reference there is a header called “sync block”. You can verify it with the following code and WinDBG:

Attach WinDBG after seeing “Foo created” but before typing anything. You should get this:

So we can see there is an object. Let’s examine it:

And now let’s see raw bytes:

Notice that I subtracted 4 bytes (this is 32-bit application). You can see Method Table pointer and 4 zeros just before that.

Let’s now continue our application and take a lock.

Type anything, hit enter, see “Foo locked”, break again and repeat last action:

You can see that now there is value one before MT pointer. This is called “thin lock” which embeds locking information as a bit pattern.

Sync block is also used for caching hash code, COM interop and other things. Because there are only 4 bytes and we cannot put everything there, at some point it will get inflated so the sync block will be a pointer to the sync blocks table. You can read more here.

39. How many app domains are there by default?

3, as shown by WinDBG:

These are:

  1. System app domain — loads mscorlib, creates OutOfMemoryException, StackoverflowException and ExecutionEngineException (so they can always be thrown, even if there is not enough memory) and manages string interning
  2. Shared app domain — contains mscorlib, types in System namespace and some code
  3. Default — contains user code and resources
40. Does a value type have a method table?

Of course it does, somehow you need to be able to call virtual methods and box objects. See this code:

Using WinDBG:

However, WinDBG is clever. See this:

So when we try to dump the object it indicates that it is incorrect. Why? Well, let’s see raw content:

So it shows value, not the reference. The pointer is a bit misleading. Let’s dump the whole stack around the function:

And now let’s dump the raw pointer (did you notice that the address is 0x2be23b8 – 4 ?)

And now you can clearly see that the MT address is the same as shown earlier.

There is a lot of trickery around value types, especially when it comes to enums. See this post describing some of it.

41. What is the base class of the delegate?

See this.


It is MulticastDelegate which in turn inherits from Delegate which inherits from object. MulticastDelegate contains a list of delegates.

42. What is the base class of an array?

See this code:

It shows the following:

So whenever you create an array, .NET creates a class for you and implements quite a lot of interfaces. But is it always the same? Check this code:

It shows

We don’t have generic interfaces here. What about pointers?

Again, no generic interfaces.

43. Can you make a 2-based array?

Use this code:


You can see that accessing index 0 throws exception.

44. What is a string interning?

It’s a way of deduplicating strings in your application. You can ask .NET to check if a string of given content was already created and reuse it. See this:

This prints: