[Originally Written October 2004 - Updated September 2009]
Many people state that Microsoft .Net technology provides a "Virtual Machine" environment via the CLR. However, an examination of various definitions of Virtual Machine shows that this is not the best analogy.
For our first example definition, let us look no further than Microsoft's own site:
Virtual Machine: A software-implemented computer that emulates a complete hardware system in a self-contained, isolated software environment and runs its own operating system.
Clearly this does not apply so lets break down the parts of "a complete hardware system". The three major categories of devices that make up a system are: Memory (some type of storage), Processing, and Input/Output.
When a program written in any language uses the Microsoft Implementation of the CLR, it directly utilizes the actual memory presented by the underlying system, All processing is done using native instruction execution on the underlying processor, and all Input/Output is accomplished via the device (Drivers) provided by the underlying operating system.
So, while it is possible (and there are projects attempting to reach this goal) to implement the CLR as a Virtual Machine. It is clear that the current Microsoft implementation provides none of these features.
The feature that the CLR provides is that there is an intermediate stage where the source code has been reduced from the original form into a well defined set of intermediate instructions that are independant of any specific target environment. Additionally a rich library (the BCL) is provided for addressing many common constructs and providing additional abstractions over lower level functionallity.
But remember this intermediate code NEVER executes. It undergoes a second compilation phase to become pure native instructions.
This process actually has a long history. Long, long ago [1970's 1980's] it was quite common for high level language compilers to emit (either by default or as an option) assembly language SOURCE rather than object code. This output could then be copies to various target machines (with differences in capabilities) and run through the assembler (often with differing configurations or external linkages) to produce an executable that was specifically tailored to the targeted machine.
Although the mechanics are different this is completely analogous to what happens with a CLR based program.
The result is that while .Net (the CLR) does provide a level of abstraction from the actual executable code, it does not meet the criteria for a "Virtual Machine".