The CLR executes only unmanaged machine code. The first time a method is called, the CIL for that method must be verified and compiled by the JIT compiler. If what the CIL does is not safe, a VerificationException is thrown. Some of the benefits of a JIT compiler are:
- Locality of reference. There is a good chance method will be on the same virtual memory page, preventing expensive page faults.
- Only the methods that are used are compiled, reducing memory pressure.
- JIT compiler can take hardware specifics into account producing more optimized native code.
The CLR allocates an in-memory data structure for each type it initializes. As of CLR v1.0 this data structure is called CORINFO_CLASS_STRUCT, and the type-handle references this data structure. Let us look what this structure looks like before and after the methods has been JIT compiled. The CORINFO_CLASS_STRUCT contains both static and instance methods.
As you can see from the picture above. Only “Method 3” and “Constructor” has been JIT compiled and execution can jump to the compiled x86 code. The other methods each call the JIT compiler stub “PreStubWorker” method. So, when one of these uncompiled methods are called, the “PreStubWorker” method is called first to verify and compile the given method. A class with three methods, initialized and fully JIT compiled would look something like this (seen from “Method 1” perspective).
Notice from the picture above that the object reference points to 4 bytes before the objects fields on 32-bit systems, and 8 bytes before the objects fields on 64-bit systems. This field is the type handle. A handle that points to the CORINFO_CLASS_STRUCT, that represents the object type. When the type implements one or more interfaces, the memory layout looks a bit different.