I beg to differ, JVMs had to introduce hotspot compilers whereas CLIs have not because of a subtle design difference between the languages and, moreover, other design differences can lead to much more efficient AOT‘d C# code. JSR294 (modularity system for Java), however, provides an opportunity to provide just as efficient AOT for Java, I hope we take this opportunity.
The subtle but vital design difference, that led to JVMs requiring hotspot compilers and CLIs not, is that non-virtual is the default in most CLI languages which is the complete opposite to Java. Most methods will never be overridden (by, for example, a method in a subclass) however in Java the programmer must explicitly state this via the final keyword. On the other hand, in C# the programmer has to explicitly declare the opposite i.e. when a method can be overridden, by using the virtual keyword. If no declaration is placed on a method in C# then it is assumed to be non-virtual i.e. the equivalent of final in java.
So why is this important? Well it turns out that one of the key qualities that must be calculated for a method to be an inline candidate is whether it is overridden. As most programmers don’t say anything about whether a method is virtual or non-virtual (we’re a lazy bunch), the default applies and as a result many more methods in C# are implicitly non-virtual i.e. they can never be overridden. The compiler can use this information when determining whether to inline. In Java much more complicated calculations must be performed and it is the costly nature of these calculations that created the need for hotspot virtual machines.
Early JVMs (i.e. pre-HotSpot) used to JIT compile every method on first use, just like CLIs do today. However, the early JIT compilers were conservative in making inlining assumptions because they didn’t know what classes might be loaded later, and as such their performance was pretty bad.
So why were they conservative? Think about what they have to do. They can’t rely on the fact that non-virtual methods are declared as such because most of them aren’t (lazy programmers and virtual is the default in Java). Instead they have to guess. To make that guess they have to understand the class hierarchy at the time of JITing and check that non of the subclasses override the method. That’s expensive enough for the inline operation, but it gets worse. They have to remember this guess in case a class loaded later overrides the method forcing them to de-optimize the code by undoing the inlining. All of this cost is significant and so hotspot compilers were introduced that only do this on code that gets used a lot.
Contrast that with the CLI implementations (i.e. mono and .net). There’s a description of the rules that microsoft uses and they are very simple. One rule is even "Virtual functions are not inlined". Whereas JVMs go to great lengths to determine whether a virtual function can be inlined, .net doesn’t even bother looking and this is all due to differences in the language specifications of Java and C#. As an aside Visual Basic also has non-virtual as the default and the overridable keyword is used to make a method virtual.
Now the JSR294 expert group (modularity system for Java) can’t make non-virtual the default in Java, but they can probably do enough that it won’t matter. If the goal is to get to efficient AOT compiled code, which is the key differentiation between CLIs and JVMs at the moment, then that goal is attainable via JSR294.
As Patrick pointed out there are various attempts to add AOT compilation to Java. GCJ and Excelsior are the two front runners. However, they have to answer this same question i.e. when can a method be inlined? Given that they do compilation ahead of time, the performance of the actual compilation step is not as critical as it is for JIT compilers and so they could use classic techniques such as rapid type analysis (RTA) or class hierarchy analysis (CHA) to determine whether a method is overridden. Unfortunately neither of these classic techniques can be applied ahead of time to Java, although they can be applied to C#, why is this?
Java’s custom classloaders mean that the class hierarchy can only be determined at runtime. This is due to a custom classloaders ability to load classes from anywhere on the classpath at any time. It’s simply impossible to understand ahead of time what the class hierarchy looks like and so RTA or CHA can’t be used for AOT compilation of Java programs. Instead to maintain Java compatibility GCJ, for example, resorts to an ABI that doesn’t even allow inlining. It’s performance suffers accordingly.
This is the problem that JSR294 can choose to solve, it can define a modularity system that allows for efficient AOT of Java code. This problem has already been well researched (and solved). MJ, a paper by, amongst others, the inventor of RTA, has already defined such a modularity system and successfully applied it to Tomcat.
Again I urge the JSR294 expert group to consider taking on the requirement of producing a modularity system that allows for efficient AOT compilation of Java code. JVMs are at a significant disadvantage compared to their CLI counterparts at the moment as efficient AOT of CLI code is already possible. Why not bring this possibility to the Java platform in JSR294?