Level: Beginner
The Common Language Runtime (CLR) provides a new technology for integrating software components from multiple organizations. The CLR provides a pervasive type system that spans programming language and operating system boundaries.

While the other tracks focus on various ways of communicating with the world outside the CLR, this track focuses on the architecture of the CLR itself: from assemblies, threading, and garbage collection, to security, debugging, and techniques for interoperating with your existing COM components.


If you need absolute mastery over the .NET CLR, only one book delivers what you're looking for: Compiling for the .NET Common Language Runtime (CLR) by John Gough.

Microsoft's long-term decision to replace COM with the CLR, you should strive to understand the underlying advantages of migrating from the old runtime environment to the new one. The architects that designed the CLR and the .NET platform were able to incorporate the best aspects of COM while alleviating much of the pain of writing and deploying COM-based applications.

  • Vastly simplified development
  • Seamless integration of code written in various languages
  • Evidence-based security with code identity
  • Assembly-based deployment that eliminates DLL Hell
  • Side-by-side versioning of reusable components
  • Code reuse through implementation inheritance
  • Automatic object lifetime management
  • Self describing objects

Code which targets the CLR can be run on several platforms

CLR Architecture

Code in the form of IL must undergo JIT compilation

 

The Common Language Runtime (CLR)

The CLR is the heart and soul of .NET. The CLR is the runtime.  It loads your code, manages it, runs it and provides a number if support services. Some of these vital support services include resource management, thread management, remoting, as well as enforcing code safety and security constraints. Code that is loaded and running under the control of the CLR is referred to as managed code. Compiled code in .NET does not contain assembly language instructions. Rather, code is compiled into assemblies that contain Microsoft Intermediate Language (MSIL). MSIL is a low level language, similar in idea to Java byte-code. The MSIL is NOT interpreted. It is JIT-compiled into native machine code.

 The runtime offers an impressive list of features.  These include self-describing components through the use of metadata, trust and security sandboxing, memory management, cross-language integration, simple deployment, and versioning.


When I said above, that code consists of MSIL, I didn’t state the whole picture. In addition to the program’s logic, .NET compilers all emit metadata. So when a PE file (DLL or exe) is created into an assembly, that file will also contain metadata. That metadata describes the types, members and references in the code. What is the metadata used for? Well, one use is that the CLR uses it to locate and load classes, prepare space in memory, resolve method invocations, generate native code and enforce security constraints. An assembly is a group of resources and types, along with metadata about those resources and types that is deployed as a unit. The metadata is called an assembly manifest and includes information such as a list of types and resources visible outside the assembly. The manifest also includes information about dependencies, such as the version of the assemblies used when the assembly was built.


This metadata used to describe types, members and references is done in terms of the Common Type System (CTS). The CLR depends on the CTS. The CTS describes a standard set of types and rules for creating those types. The .NET languages must conform to the CTS. The CLR knows how to make and manage these types and provides these services to the languages. This is also what is meant by self-describing components. Since the metadata travels with the assembly, an assembly is completely self-describing. No more type libraries!

This type system provides the basis for the CLR’s impressive multi-language capability. An object can be defined in VB.NET, and then called from a C# object, which is then called from an Eiffel.NET object. It is easy to design components and applications that interact across languages. . You can also pass an instance of a class to a method of a class written in a different language. This cross-language integration is possible because language compilers and tools that target the runtime use a common type system defined by the runtime, and they follow the runtime's rules for defining new types, as well as for creating, using, persisting, and binding to types. Separate IDL files, type libraries, or proxy/stubs are not required to access a component across language or process boundaries; the necessary information is located in the component's metadata.

Assemblies can be private to an application or shared by multiple applications. Multiple versions of an assembly can be deployed on a machine at the same time. Application configuration information defines where to look for assemblies, thus the runtime can load different versions of the same assembly for two different applications that are running concurrently. This eliminates issues that arise from incompatibilities between component versions, improving overall system stability. If necessary, administrators can add configuration information, such as a different versioning policy, to assemblies at deployment time, but the original information provided at build time is never lost.
Because assemblies are self-describing, no explicit registration with the operating system is required. Application deployment can be as simple as copying files to a directory tree...). This is what is meant by x-copy deployment. Configuration information is stored in XML files that can be edited by any text editor.

Perhaps the most touted and needed service of the runtime is of automatic memory management. The runtime automatically handles object layout and deletion and reclamation of memory resources through the use of garbage collection. Objects that are no longer in use are released automatically. This, of course, eliminates memory leaks as well as some programming errors.

Finally, the runtime also supplies integrated, pervasive security services to ensure that unauthorized users cannot access resources on a machine and that code cannot perform unauthorized actions. This improves overall system safety and reliability. Since the runtime is used to load code, create objects, and make method calls, the runtime can perform security checks and enforce security policy as managed code is loaded and executed.

The CLR Execution Model

As stated previously, the .NET compilers spit out MSIL to an assembly. What is this IL? Well, its simply instructions for a virtual machine, the CLR. It is always executed indirectly through the use of a Just-In-Time (JIT) compiler. The JIT compiler knows how to translate the IL into native machine instructions for whatever platform the CLR is implemented on. Notice that I said “whatever Platform.” There is nothing in the CLR design or .NET for that matter that assumes Windows. The CLR is portable by design and we may see ports to various platforms in the future. Its also important to note that the machine code is not saved to a file, and is cached, such that the program can be re-executed.

The assemblies are usually demand-loaded and then JIT-ed at the time of loading. At load time, the assembly goes through some level of verification. The most basic check is that the CLR has to be able to make sense of the IL in order to generate machine code. Higher levels of checking enable the execution engine to ensure that the assembly is memory-safe.