I suggest starting a new question with the subject that interests you most, referencing this question. That way it can cover each subject more deeply, if there are experts around
–
Vinko VrsalovicSep 14 '08 at 1:51

9 Answers
9

Already posted this once but here is a series of interviews with c# chief language designer Anders Hejlsberg. Though mostly talking about the differences between C# and Java he does dive into differences between the virtual machines as well.

There are a number of fundamental
technical similarities between the
Java Runtime and the Common Language
Runtime, including garbage collected
memory, an intermediate language
(Microsoft IL versus Java ByteCode),
core system libraries, and support for
fairly high level languages, code
security, and deployment.

However, each of these 'similar' areas
also have a number of sizable and
small differences, and it's beyond the
scope of a simple Forum post to
describe most of them.

I would suggest asking a more
targetted question about any of the
various runtime features and component
areas (e.g. memory management,
compilation, system libraries,
security, etc.) and then we can
provide a more targetted response
(e.g. a blog, a technical article, or
some books).

Since the CTS and the CIL which comprises the non-implementation details of the CLR are an open standard, as now is the JVM, the only real difference between them from a portability standpoint is that more vendors implement a JVM than a CLR. Not insignificant, especially given the pragmatics of MS having the de-facto implementation, but it's not as if we are dealing with open vs. proprietary code here.
–
codekaizenDec 17 '09 at 3:13

Java might be portable across many platforms, but it is not trivially portable, at least across Linux and Windows in my experience. I work for an e-commerce company and one of the products we use to power our website has given us a lot of grief and the vendor keeps complaining about us running their Java/Linux product on Windows servers.
–
Umar Farooq KhawajaJul 25 '10 at 7:15

Seasoned industry programmers will notice that the above is
very much like Java and the Java VM. They are right, the above
is just like Java.

The CIL has one feature not found in Java though: it is
byte code representation that is powerful enough to be used as a
target for many languages: from C++, C, Fortran and Eiffel to Lisp
and Haskell including things like Java, C#, JavaScript and Visual
Basic in the mix.

I wish I had the time to go in more detail, but for the sake
of this argument, the above will suffice.

The comments go into some details, though, like tail call optimizations. Lot have changed since 2002 though - both CLR and JVM now have multiple languages targeting it. But nonetheless worth a read.

hm... i'm not sure that it's quite relevant, et least nowadays (the answer was posted 2 years ago). For now we have a Clojure, which is a dialect of Lisp, and still have Rhino (which is a JavaScript), and numerous languages, such as Groovy and Scala.
–
0100110010101Sep 10 '10 at 14:10

The question of whether to tail-call optimize or how to inline things is an interesting one, since there can be some advantages to having a stacktrace that is guaranteed to represent reality. One could use metadata to cause inlined routines to appear on a stack trace as calls, but tail calls would require the generated code to keep track of the nesting level. Using a local variable for that purpose may be cheaper than using nested 'call' instructions, but one would then have to decide how to represent it on the stack trace. If recursive depth is limited to actual stack...
–
supercatFeb 4 '13 at 16:12

...that puts a reasonable limit on how long a stack trace can get. By contrast, if a routine could tail-call recurse millions of levels deep, expanding out those calls in a stack trace would be absurd.
–
supercatFeb 4 '13 at 16:13

The CLR and JVM have goals and philosophies that differ more than you might think. In general, the JVM aims to optimize more dynamic, higher-level code while the CLR gives you more low-level tools to do these kinds of optimizations yourself.

A good example is stack allocation. On the CLR you have explicit stack allocation of custom value types. On the JVM the only custom types are reference types but the JVM can convert heap allocations to stack allocations in certain circumstances through Escape Analysis.

Another example. In Java, methods are virtual by default. On C# at least, they are not. It is much more difficult to optimize virtual method calls because the code that gets executed at a given call site cannot be determined statically.

Under the hood, their execution systems are quite different. Most JVMs (in particular, Hotspot) start out with a bytecode interpreter and only JIT-compile parts of the code that are executed heavily e.g. tight loops. They can also re-compile these over and over each time using execution statistics collected from previous runs to drive optimizations. This allows more optimization effort to be applied to the parts of the program that need it most. This is called adaptive optimization.

The CLR compiles everything up-front only once. It does fewer optimization both because it has more code to compile and so has to be fast and because it doesn't have any statistics of the actual execution paths taken to feed into its optimizations. This approach does have the very significant advantage of allowing you to cache compilation results across processes, which CLR does but JVM does not.

A large percentage of the Hotspot JVM code is dedicated to these adaptive optimizations and they are what put Java in the same performance ballpark as native code for most general purpose computation in the early 2000s. They are also what makes the JVM a decent target for dynamic languages. I'm excluding here the more recent developments of the Dynamic Languages Runtime and invokedynamic as I don't know enough about the DLR.

As Vinko said, the full details are way beyond the scope of a forum post. The differences/similarities boil down to this:

They are both a runtime environment "sandbox" that include a "just-in-time" compiler to translate program instructions in an intermediate language (MSIL or ByteCode) to native machine code and provide automatic memory management (garbage collection). Sitting on top of the respective runtime environments are a set of class libraries that provide higher level abstractions to developers to simplify development tasks.

The internals of how those runtime environments are actually implemented are, for the most part, proprietary to Microsoft and Sun. The algorithms used by the garbage collection systems, for example, while probably similar in technical functionality are different in implementation.

Also type erasure mentioned by Flyswat is important. JVM doesn't have a clue about generics and everything is object and associated perf. penalty of boxing and unboxing. Also reflection won't give you generic information. CLR supports generics natively.