CLR vs JVM: How the Battle Between C# and Java Extends to the VM-Level
The battle between Java and C# is never ending. Find out how differences at the VM-level affect this debate.
We’ve written extensively on the differences between C# and Java, covering our favorite features in each that don’t have an equivalent in the other. With the fierce battle over which language is better than the other raging on, we’re diving deeper into the battleground to look at the differences and similarities between their respective Virtual Machines (VMs).
TL;DR: The Top 5 Similarities and Differences Between the CLR and the JVM
Top similarities between the CLR and JVM include:
- Both Virtual Machines (VMs)
- Both include garbage collection
- Both employ stack-based operations
- Both include runtime-level security
- Both have methods for exception handling
Top differences between the CLR and JVM include:
- CLR was designed to be language-neutral, JVM was designed to be Java-specific
- CLR was originally only Windows-compatible, JVM works with all major OSs
- CLR uses a JIT compiler, JVM uses a specialized JIT compiler called Java HotSpot
- CLR includes instructions for closures, coroutines and declaration/manipulation of pointers, the JVM does not
- JVM is compatible with more robust error resolution and production monitoring tools
Now that we’ve established the 5 top similarities and differences, let’s jump into what all of this means. Starting with the basics, and then diving deeper into what all of this means.
What’s a Virtual Machine?
It’s not hard to guess that a virtual machine (VM) is a virtual emulation of a computer operating system. Beyond that, though, there are two different implementations of this kind of system abstraction.
System virtual machines exist as fully-functional operating systems and are typically created as a substitute for using a physical machine. In this way multiple environments can run on the same machine simultaneously. But, that doesn’t sound like our beloved CLR and JVM.
The Common Language Runtime (CLR) and Java Virtual Machine (JVM) are both process virtual machines. That means that rather than executing an entire operating system, they are specifically designed to run a process or program without being dependent on the platform environment.
A Closer Look at the Similarities Between the CLR and JVM
The CLR and JVM are both virtual machines and they both perform platform-independent operations, namely, executing compiled source code. Essentially, these virtual machines both act as an intermediary step between the programmer’s source code and the system’s machine code, allowing for greater usability by various types of processors. In the image below, you can see how the high-level functions of the two are practically identical.
Beyond their basic functionality, both also include methods for garbage collection, runtime-level security and exception handling. And one last similarity to mention, both employ stack-based operations, the most common approach to storing and retrieving operands and their results.
But, of course, for every similarity that these VMs share, a difference in implementation can be found. Still, just as with programming languages, the development of these VMs advances in a kind of leapfrog-like motion. One implements something like the “mark-sweep-compact” approach to garbage collection, and the other is soon to follow.
And Now Some Major Differences
Language and Platform Compatibility
One potentially superficial difference between the CLR and JVM (though one that likely influenced the way they developed) is that the JVM was created to work specifically with Java, while the CLR was designed to be language-neutral. Conversely, the CLR was originally designed only to run on Windows OS whereas the JVM was always OS-neutral. Times have changed though, as we all know, now there is CoreCLR which runs on Linux and Mac, and many more languages have been developed to work with the JVM.
This leads to the fact that, for the most part, the differences between the CLR and JVM are also signifiers of the differences between the languages that employ them. Or, you can say that some of the most significant differences between languages (for argument’s sake, let’s say C# and Java) really are implemented at the VM-level.
A big difference that we see at the VM-level is that although both use JIT (Just-in-Time) compilation, the compiler isn’t called to run at the same time. The CLR compiles all MSIL code into machine code when it is called at runtime. The JVM uses a specialized performance engine called HotSpot to do JIT compilation of Java Bytecode into machine-readable code. It differs in that it compiles and optimizes the “hot spots” in the code that are used most frequently.
Each of these compilation strategies has its own tradeoffs in terms of performance. Because the CLR compiles all of the machine code once when it’s called at runtime, execution time can improve in some circumstances. But on the other hand, if a small portion of the code will be needed at a high frequency, Java’s HotSpot compiler can improve efficiency over time with additional optimization.
Implementation of Language Features
Another smaller difference is that the CLR was built with instructions for dealing with generic types and for applying parametric specializations on those types at runtime. Basically, that means that the CLR recognizes the difference between, for example, List<int> and List<String>, whereas the JVM can’t (Java implemented Generics as part of the compiler). The CLR also allows users to define new value-types in the form of Structs, while value-types in JVM-based languages are fixed (byte, short, int, long, float, double, char, boolean), though there are plans in the works to change this.
Production Monitoring and Exception Handling
Although both include methods for exception handling, overall differences between the two can affect compatibility with different exception- and error-monitoring tools. This, in turn, affects troubleshooting strategies and workflows.
Performance and Adoption
A 2003 study by a Jeremy Singer at the University of Cambridge looked at the performance differences between the CLR and JVM and found that, at least at that time, that their performance was practically identical.
The study found that the Java compiler produced smaller bytecode, which can have an impact on VM performance. However, when it came to running code, the execution times came out pretty much 50/50, with the CLR and JVM each executing faster about half of the time. Based on this study, when it comes to the fierce opposition between Java and .NET users, it looks like the VMs themselves play a bigger part in differentiating capability than they do in directly affecting application efficiency.
This also make sense when looking at adoption. In enterprise companies, it’s not uncommon for developers to work with both CLR- and JVM-based languages. This can happen especially in large companies with many different departments working on various projects and applications. For example, Accenture uses both C# and Java, as does Intuit.
Speaking of the competition, we know that .NET and Java both have strong communities backing them up. Within those communities, developers ask questions and engage in in-depth conversations on sites like StackOverflow. A quick search for the name of each VM reveals that CLR has been tagged 3,250 times compared to the JVM which has been tagged 8,628 times.
Outside of StackOverflow, there are also extensive communities that are cultivated by Microsoft and Oracle themselves. There, users can find additional information and resources related to more than just the CLR and JVM. Topics there include implementations in the cloud, troubleshooting questions and more.
Beyond this, though, the communities are definitely more centered around individual languages and platforms such as .NET, Java, C/C++, Scala, etc.
Looking at these VMs at the highest level, the differences between the CLR and JVM seem almost negligible. However, in many (if not most) cases, the differences at the VM-level mirror the key differences between the languages that use them. Because of the way these VMs, and their corresponding languages, were built, each functions slightly differently in order to provide the functional capabilities that their creators wanted to provide.