How does method execution differs when CLR handles static, instance and virtual instance methods ? To answer them, one needs to begin by understanding a bit more on how CLR handles Managed Heap during execution of a method.
Memory Allocation in Managed Heap
When the application execution begins, as the CLR gets loaded, the thread stack and managed heap gets allocated. Prior to method execution, JIT (Just In Time) Compiler converts the IL code to native instructions. At this moment, it also detects all the types used in the method and also loads all the required/dependend assemblies needed to support the execution.
The CLR also gathers information about the data types involved from the metadata and create the required Data structures. Each time a new Type is encountered, a data structure representing Type Object is created. The Type Object contains two special fields called Type Object Pointer and Sync Block Index. The Type Object also contains static fields and a method reference table. The Method table contains an entry for each method exposed by the type.
For each instance of a type, an instance object data structure is create in the Managed Heap. The instance object would memory space for storing the instance fields in the type. Similiar to the Type Object , the instance object would also contain the two special fields – Type Object Pointer and Sync Block Index.
The Type Object Pointer of the instance object points to the corresponding type object. At this point, one might wonder what would the Type Object Pointer of the type object point to ? Well, it points to a special type object of type
System.Type. The next question that might arise in your mind would be, what would type object pointer of the
System.Type type object refers to ? The answer is – it refers to self. Interesting.
Once the local variables are created in Thread Stack, the compiled native instruction is executed. After the method exection completes, the CPU instruction is set to the return address.
A common question that might arise would be what happens when a new object is created using the
new operator ? During the object creation, the CLR calculates the size of instance fields in the type. It then allocates memory for the calculated size, including fields for Type Object Pointer and Sync Block Index. It also resets the memory to zero. At this point, the constructor is invoked and any fields that could be initialized are assign initial values. It then returns the reference(address) to the newly created object.
Let us now see how CLR handles method execution and how the data structures we discussed in earlier section are related.
Static Method Execution
During the execution of Static Methods, CLR locates the concerned Type’s Type Object data structure in the heap (it would create it if not already created). It then proceed to locate the entry in the method table for the method to be invoked. The compiler then JIT Compiles the method code (only if required – if the method has already been invoked and the native code is already available, this step is skipped) and start executing the native code.
Non Virtual Instance Methods
The execution of a non-virtual instance method is slightly different from the execution of Static Method. It has couple of additional steps.
During the execution of non-virtual instance methods, the compiler detects the Type Object that corresponds to the type of variable being used in the method call. If the method entry is not found in the method table, then it walks through the class heirarchy, checking each of the base type objects for an entry in the method table.
Once it finds the method, follows the steps mentioned for static method. It JIT compiles (only if required) the method code and invokes the native code.
Virtual Instance Method
In the case of virtual instance methods, the compiler begins by refering to the address of instance object the variable points to. The Type object pointer of the instance object would be pointing to the actual type of the object. It then locates the method entry in the Method table, JIT compiles if required and start executing the code.
A summary of the whole process is visualized in the sketch notes below.