15

This question already has an answer here:

Java has been designed to run on a virtual machine to allow portability for programs.

However .NET has been designed from the start specifically for Windows.

Than what is the reason for .NET applications being compiled to bytecode for the CLR?

Was it simply to copy Java? Or is there a technical advantage to just compiling natively?

marked as duplicate by gnat, durron597, Eric King, Kilian Foth, gbjbaanb Sep 29 '15 at 8:28

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 24
    You are confused, "Windows" is not a platform which implies one and only one specific processor architecture. Moreover, your premise is wrong. – Doc Brown Sep 28 '15 at 8:24
  • 5
    You can even run windows 10 on the Raspberry Pi. – user40980 Sep 28 '15 at 13:27
  • 14
    If you rephrase your initial statement to "Java uses a virtual machine architecture to lower the costs of creating portable programs", does your question about the CLR answer itself? – Eric Lippert Sep 28 '15 at 13:41
  • 2
    @gbjbaanb heh. Its more intended for The Internet Of Things as the underlying operating system (rather than Linux). I don't foresee it being used to run Word on my refrigerator, or thermostat. – user40980 Sep 28 '15 at 14:26
  • 8
    But, I want to run Sharepoint on my toaster :( – MetaFight Sep 28 '15 at 14:28
37

Compiling to some bytecode is an old tradition. UCSD P-code existed in 1978, and had many precursors. Today, LLVM can be seen as a bytecode, targetted by Clang/LLVM ahead-of-time compiler suite and GCCJIT can be viewed as a JIT related to GCC (with GIMPLE sort-of being some internal bytecode).

(hence, bytecode, JIT, ... has quite fuzzy meanings today; JIT's broadest sense is compilation inside the process running the compiled code.)

The JVM bytecode was initially implemented as interpreter. But Java become popular enough to get JIT based JVMs (and Sun invested a lot in JIT technology, so this helped Java to become successful).

And JIT existed long time ago (in the early 1980s, e.g. in Lisp machines, and even in 1960 on the CAB 500 computer and others), before even the name was used. Many Common Lisp or Smalltalk implementations had JIT compilers (and today, SBCL is fully JIT-ing).

In my understanding, Microsoft designed the CLR bytecode to be JIT compiled (hence got different tradeoffs in its bytecode than the JVM). And it has recently published its implementation as open-source software and ported it to Linux (before that, Mono existed on Linux).

A bytecode is often more compact than native binary executables, it can be made portable to several architectures (e.g. x86 32 bits and x86-64 and also ARM 32 bits, ARM/Aarch64, ...) and might be designed to avoid (or at least soften) dependency hells.

A big advantage of JIT compilation is that the VM can recompile some parts of the bytecode based upon dynamic contextual information (e.g. profiling, call stack introspection, ...) some code. Some JIT-ing infrastructures like libjit, asmjit, LLVM, GCCJIT, ... don't do that (however, the implementation using them could do that by repeated use of the JIT-ing infrastructure), but most industrial JVM or CLR implementations do it (and some people call JIT only that lazy on-demand dynamic compilation; for me JIT is just a buzzword for dynamic compilation at runtime). This is difficult or impossible with AOT compilation (at least, requires LTO), and is impossible if you want to do profile-guided optimization dynamically at runtime (as most JVM or CLR JIT implementations are rumored doing). Also, a bytecode VM don't need to JIT-compile all the bytecode, but only the most used parts (as HotSpot does) and keep interpreting the rarely used cold code.

Also JIT implementations can cooperate much more (and better...) with sophisticated garbage collectors.

PS. I know nothing about Windows. I never used it. I'm using Linux since 1994 and Unix since 1987.

  • 2
    It's interesting to note that even in the 1960s, the Apollo guidance computer used a somewhat similar scheme. See here on NASA's History site, the section titled "Programming the AGC". I recall seeing something more thorough on Wikipedia but can't seem to find it. – a CVn Sep 28 '15 at 13:35
  • 1
    Without going as far as x86_64 to ARM, each stepping in the x86 or x86_64 families introduced new features (e.g. vector instructions) that you would have to ignore for the sake of retrocompatibility when distributing a statically compiled executable. A JIT machine can take advantage of those, it's like having -march=native always on. – Stefano Sanfilippo Sep 28 '15 at 14:18
  • A big advantage of JIT compilation is that the VM can recompile parts => while very difficult to impossible with native code, simple delivering LLVM IR and generating binaries from that would be possible. Actually, the Mill CPU creators plan on doing something similar for their CPU: write target-agnostic code, and a specializer on the machine generates machine-specific native code (with all the available instructions). It's still AOT, since you generate before starting execution. – Matthieu M. Sep 28 '15 at 14:40
  • 2
    I'm not seeing how this differs from a VM in anything other than eagerness of bytecode->native translation. But then I would label x86 as a VM nowadays, with numerous hardware implementations from Intel and AMD – Caleth Sep 28 '15 at 15:32
  • 1
    @MatthieuM. I've never heard of the Mill CPU, but the Transmeta architecture took a very similar approach, with VLIW instructions although they use dynamic compilation and caching instead of AOT. The few Transmeta x86 laptops appeared on the market achieved higher performance with very low power compared to that time's Intel CPUs – phuclv Sep 29 '15 at 6:28
23

Windows is not a single, homogenous platform. When the first version of the clr was released, it targeted not only the traditional windows 98 family of systems (which only ran on x86) but also windows nt 4 (x86, ppc, alpha, mips). Support for windows ce (which ran on x86, sh, arm, mips and ppc) was added in version 1.1, while ia64 ("itanium") and x86-64 targets on nt were added in version 2. It is quite likely that the developers knew that most or all of these platforms would need to be supported when the project began. In fact, it seems probable that the fact that ISVs weren't keen to support this many hardware platforms and tended to only release x86 versions of applications figured in microsoft's decision to go ahead with developing the system.

  • 1
    An interesting aside: initial Windows NT development specifically targetted non-IA-32 platforms. – a CVn Sep 28 '15 at 13:24
  • @MichaelKjörling I worked at SGI at the time when you could run Windows NT on MIPS systems in a supported configuration by SGI itself. I recall Windows NT on Alpha systems prior to graduating from the university too (back in '95). – user40980 Sep 28 '15 at 13:25
  • @MichaelT The NT 4 installation CD had binaries for a number of architectures. I believe Alpha was one of them. – a CVn Sep 28 '15 at 13:37
  • 4
    Not only was it possible, but the DEC Alpha machines were the fastest NT machines around. – MSalters Sep 28 '15 at 15:10
9

Eric Lippert has a good explanation of why the .NET languages target IL instead of directly generating binary files titled Why IL?.

The reason is that the cost/effort of developing your compiler is cheaper/simpler with this approach. Your high level language compiler generates a common intermediate language. This makes it easy to add new languages as there is only one platform to generate code for. Then you have your 2nd stage compilers that take this intermediate language and output the platform specific binary (OS and CPU architecture).

This way each time you add support for a new platform (such as a new processor), you just need to write a single compiler that compiles IL to the new platform.

  • 2
    That is true for any intermediate representation, but does not address why the translation from IR to machine form happens at runtime rather than at compile time. – Angew Sep 29 '15 at 8:15
2

Many features of the .NET type system, especially the generic types which were added in 2.0 but (from what I understand) anticipated from the get-go, make it literally impossible to compile code for all of the types a program might use before it starts execution (since the combinations of type used by a program may be affected in arbitrary ways by the inputs received by a program, and although the number of discrete types actually used by a program in the course of a single execution must be bounded, the number of types that a program may use in response to various inputs need not be). While dynamic code generation is possible even without a virtual machine, using a virtual machine makes it much easier and safer.

Further, the efficiency of garbage collection in a multi-threaded environment can be improved tremendously by giving the garbage collector the ability to simultaneously block all threads that might make any use of references to garbage-collected objects. Even if the garbage collector is required to keep "stop-the-world" events as short as possible, the ability of the collector to unilaterally lock out other threads from interacting with GC references makes it possible for other threads to read and write references without having to use interlocked reads and writes. This can have enormous effects on efficiency.

Most of what the .NET VM manages to accomplish could be done without using a VM, but using a VM offers huge safety and performance benefits which far outweigh the downsides.

Not the answer you're looking for? Browse other questions tagged or ask your own question.