Compilation and Execution Process in .NET

Compiling to MSIL

When a program written in .NET aware programming language is compiled, high level program language code is converted into Intermediate Language Instructions. The output of program compilation is actually in the form of a file (with extension .dll or .exe), which is called Assembly. Assembly contains Intermediate Language Instructions, metadata, manifest and resources (if any). Assemblies generated for the same program written in different languages and compiled by corresponding compilers are almost identical and platform independent. Because of this approach of generating platform independent instructions during the compilation process, assemblies can work on any platform supported by .NET framework.

Compiling MSIL to Native code using JIT

The CLR using Just In Time (JIT) compiler, compiles the IL instructions of the method it is currently working and converts them to platform specific native instructions. JIT also caches the results for future use, so that when the method is called second time it does not have to recompile the IL. In short, JIT only compiles the methods that are required during the execution and also caches them for future use. One problem with JIT compilation approach is every process using the assembly needs to go through the IL compilation process, the cached native code is bound to the process that triggered the compilation.

Compiling MSIL to Native code using Ngen.exe

Each process using a .NET assembly needs to compile methods in assembly to native code during runtime. This effects the performance of a bit (mostly during the first run, because as we learned above JIT caches and cached native code is used on second run), which is acceptable in most cases. As the cached native code is bound to a specific process that triggered the compilation, each process need to maintain its own cached native code.

To allow multiple processes to share the native code generated for a set of assemblies .NET framework provides Ngen.exe tool. Converting IL instructions to native code before running the application (JIT does at runtime during execution) is called as ahead-of-time compilation. Ngen.exe converts IL instructions of the entire assembly to native code and persists the generated native code in the Native Image Cache as file on the disk.

Code Verification

When converting IL instructions to native code, code verification is performed. Code verification process involves, make sure that all the code is type safe (meaning the code is accessing memory locations which it is allowed to access) and verify whether IL instructions are correctly generated.

Running the code

When a program is executed the Common Language Runtime (CLR) will load the assembly into memory using mscoree.dll. Once the assembly is loaded, CLR also loads all the references of the assembly by reading the manifest present in the assembly. In addition CLR also interacts with base class libraries when needed. Each method is JIT complied as explained above and the method is run. The process of JIT compiling and running the code is done for each method until the execution is complete. In case of Ngen generated native code, JIT compilation is by passed and native code from the Native Image Cache is used for each method.