Similar to the Java Virtual Machine (JVM), the .net CLR is a byte-code interpreting virtual machine.
The JVM interprets programs which contain java byte codes and the .net CLR interprets programs which contain what Microsoft calls "Intermediate Language (IL)" instructions. There are differences between these byte codes, but the virtual machines are similar and aspire to provide similar features.
Both of these virtual machine implementations have the ability to compile their input bytecode to the machine language of the computer they are running on. This is called "Just In Time Compilation (JIT)" and the output code produced is called "JIT code." Because the JIT code contain sequences of instructions in the machine language of the computer's CPU, this code is sometimes referred to as "native" code.
However, JIT code is qualitatively and quantitatively different from native code, as explained below. For that reason, this article considers JIT code to be nothing more than a native implementation of the Virtual Machine while running a particular bytecode program.
One feature that both these Virtual Machines (VMs) aspire to provide is security in the form of preventing certain hazardous programming errors. For example, the title of this website forum, stackoverflow, is inspired by one such type of hazardous error that is possible in native code.
In order to provide safety and execution security, the VMs implement type safety at the "Virtual Machine level". Assignments to VM memory are required to store the type of data which is held in that memory location. For example, if an integer is pushed onto the stack, it is not possible to pop a double from the stack. C-style "unions" are prohibited. Pointers and direct access to memory are prohibited.
We could not get the same benefits by enforcing an object oriented language framework on developers if the result is a native binary such as an EXE file. In that case, we would not be able to distinguish between native binaries generated using the framework and EXEs generated by a malicious user employing sources other than the framework.
In the case of the VMs, the type-safety is enforced at the "lowest level" that the programmer is allowed to access. (Neglecting for a moment that it is possible to write managed native code, that is.) Therefore, no user will encounter an application which performs one of the hazardous operations which require direct access to memory locations and pointers.
In practice, the .net CLR implements a way to write native code which can be called by .net "managed" code. In this case, the burden is on the native code author not to make any of the pointer and memory mistakes.
As both the JVM and .net CLR perform JIT compilation, either VM actually creates a native-compiled binary from the bytecode supplied. This "JIT code" performs more quickly than the VM's interpreter execution, because even the machine language code produced by JIT contains all the VM's needed safety checks that the VM would perform. As a result, the JIT output code is not as fast as native code which would ordinarily not contain numerous run-time checks. However, this speed performance drawback is exchanged for an improvement to reliability including security; in particular, use of uninitialized storage is prevented, type-safety of assignments is enforced, range-checking is performed (thus stack- and heap- based buffer overflows prevented), object lifetimes are managed by garbage collection, dynamic allocation is type safe. An environment executing such run-time behavior checks is implementing the specification of a virtual machine and is little more than a machine language realization of a virtual machine.