As we all know that our .NET code (C#, VB.NET etc) are converted into MSIL instructions which in turn are put into the assemblies. MSIL is a high level language, not as low as machine instructions, so it needs to converted into machine specific binary code or needs to be interpreted somehow. Since interpretation would make the execution significantly slower, the code is converted into machine code on access. This is done via a procedure called Just in time compilation (JIT). Just in time is a management concept that was introduced by Ford Motors into production environment. This is a process where inventory was brought on time just before they were needed, and this saved the warehousing or storing costs.
How does JIT compilation happen?
In programming JIT works like this. Since the executable or the library is made of MSIL (bytecode or any other intermediate form) instruction it needs to be compiled into machine code, but if we convert all of the code into machine format then it will take time. For example if we have an application has 50 functions and we use 3 of them regularly, then if each time when we load the program and compile all the 50 functions then it would be a waste of time and would take a long time to load. What JIT does is to convert the function's MSIL into machine code just before executing the function. See the figure to see how a code is compiled via JIT. Once a code has been transformed into native machine code it stays in memory and next calls to function are pointed to that same memory so that the conversion to machine code is done only once in the lifetime of an executable.
A little deeper look: The secret undocumented CLR function that does it all
When a .NET application loads then it loads the MsCorEE.dll which loads the correct version of the MsCorWks.dll (the version of .NET we are running) which contains all core functions of the .NET runtime. For more detail on loading see the web, one good resource can be this post NET Foundations - .NET execution model. There is this function called _CorExeMain which actually loads the CLR and all the types that are required into memory. There is a memory table for types and there are tables for functions and properties of each types.
Lets say we have a class that looks like this
static void ConsoleAdd ( int value1, int value2)
Console.WriteLine ( value1 + value2);
Now a careful look would tell us that only 2 types are used here TestClass and Console. If we call the TestClass.ConsoleAdd function from the main method this is how the memory looks like before the function is called.
Before the call both the ConsoleAdd and WriteLine and other functions are pointing to the secret JITC function. This is how each time the JITC functions compiles the code into native code and replaces the function pointer in the function table for the type.
Now lets look at the memory after it has been JITed.
So now we know how our C# code is compiled in to native code. It may be hard to believe that at certain times JITed code runs faster than the native compiled code. When a code is JIT compiled it takes the advantage of the exact CPU instructions present the the machine but the native compiled code compiles into more generic class of machine instructions. We will read about advantage and disadvantages of JIT another day.