Chapter 3. CLR Internals

This chapter will guide you into the knowledge and usage of the virtual machine in which any Microsoft .NET Framework-based language can actually run: the Common Language Runtime (CLR). Good knowledge of such internal functionalities will help any programmer produce better code, avoiding bottlenecks and anti-patterns.

The important aspects that make .NET so easy to use and powerful at the same time have all been explained in depth in this chapter. Go through this exciting chapter and learn how to work with the most beautiful programming language framework there is.

In this chapter, we will cover the following topics:

  • Memory management
  • Garbage collection
  • Working with AppDomains
  • Threading
  • Multithreading synchronization
  • Exception handling

Introduction to CLR

CLR is the environment that actually executes any .NET application. A widely used definition is that the CLR is the virtual machine running any .NET application. Although this simple explanation is somehow correct, we must take a step back and explain in depth what C#, Visual Basic, and CLR are.

.NET is a managed programming language that offers the ability to program any kind of application, target any platform, abstract what is usually said to be low-level programming, such as memory management, object initialization, and finalization, and access any operating system, and so on.

C#, VB.NET, F# and many other high-level languages from Microsoft for the .NET Framework and other non-Microsoft languages such as COBOL.NET are human-oriented languages with proper design pros and cons that are usually linked to historical trade area or scientific needs. For instance, management software was usually made in Visual Basic, while low-level programming in C/C++, scientific programming was done with FORTRAN, and banking programming in COBOL.

When dealing with .NET Framework, all compliant languages (such as C# and VB) are actually only the frontend a programmer uses to interact with real .NET language, such as Microsoft Common Intermediate Language (CIL).

When a programmer builds code with Visual Studio, they trigger the compiler to produce the CIL from the source code. The compiler itself is also usable by any command prompt or script because of being a simple console application.

Together with the Intermediate Language (IL), any compiler of .NET languages also produces relative metadata that is definitely required for datatype validation in class member invocation, to reflect types, members, and so on.

This module was made by IL and metadata, together with a Windows Portable Executable (PE32 or PE32+ for a 64-bit target platform) header, defines the kind of module (.dll or .exe) and the CLR header. This header defines the version of .NET used and relative options, produce a file package known as Assembly, which also contains the eventually linked resources such as images, icons, and so on.

In the following diagram, we see an assembly with all its layers, showing the .NET physical file structure with the system headers, CLR header, code metadata, and body:

Introduction to CLR

From C# to an Assembly

Once the compilation succeeds, we can launch the application (or link the DLL as a reference to other applications) with the usual double-click. The PE32(+) header will run the executable as an unmanaged application, which will try to load the .NET environment by launching the CLR with relative configuration as available in the assembly file, such as the .NET version, requested target platform, and others. On a system without the proper .NET framework runtime available, the whole application will break execution while on any valid system, the application will run normally.

The following is a simplified block diagram that shows the CLR process execution sequence:

Introduction to CLR

The application startup lifecycle within the CLR

Once the metadata loads successfully, any method is ready to run within the Just-in-Time (JIT) compiler of the CLR. JIT compiles the platform-independent CIL language in a platform-specific optimized language that can be executed on the underlying system, method by method, in a lazy fashion. Once a method is actually compiled, this compiled code is injected into the in-memory metadata of the assembly, so as to not have to compile it again until the application remains loaded in the memory.

Although the best performance ever available is provided by using native coding, only experts are able to reach similar results. Otherwise, CLR and its JIT compilation produce great code that often performs fine (and sometimes better) than compared to any unmanaged application, if mid-level programmers are involved in the coding. This is because of the great optimization work done by Microsoft when converting CIL to native code.

This comparison is similar to what happens if we compare an OR/M (such as Entity Framework) querying performance with one of the stored procedures . Although the best-ever results are obtainable only by using specific DBMS features available with specific dialect-SQL coding (such as T-SQL, PL-SQL, and so on), only an expert SQL developer is able to provide such kind of querying.

A moderately experienced C# developer is more able at object querying (and such querying is more platform- and database-producer independent) than in specific SQL coding. This is why, for the most part, object-querying will always produce better performing queries, compared to SQL querying performances. In the future, relational databases will be superseded by NoSQL databases. So, for young developers, learning SQL coding is something actually secondary in their professional growth schedule.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.101.81