JVM hate

|
|

I came across this paper, published by Google, comparing benchmarks in Scala, C++, Java, and Go.  Scala comes out on top (well, under C++) in performance, by a decent margin.  This surprised me a bit, but makes me happy – Scala is a functional language, and I do think functional languages are the (medium) future of software development.  My main beef with Scala is the JVM.

There are (in my mind) several problems with the JVM.  First, Java is now owned by Oracle, who’s begun suing companies using Java technology.  This isn’t like Sun’s lawsuit against Microsoft way-back-when, which was about maintaining compatibility in the JVMs so as not to fragment Java; no, this time, it’s about money: Oracle wants a piece of the Android pie.  The lawsuit makes good business sense for Oracle, but it puts an indelible stain on Java and the JVM, and one that I can’t abide.  Now that Oracle has proven its willingness to go after companies using Java technology for financial gain, no company is safe, and every company should seriously consider whether using Java (or a JVM) is worth the financial risk.

Second, the JVM is a bloated hog.  Yes, it is fast. It’s amazingly fast. The scientists at Sun have done a great job with JIT optimizations, bringing Java performance up into the top tier of languages (grain of salt, but real-world reports of Java performance are pretty consistent).  However, it’s a memory hog, and it can be difficult to tune the garbage collection so that unexpected spikes don’t occur, or applications don’t crash with out-of-memory exceptions, and so on.  Its start-up time is also a problem, and there’s on old adage of “write once, debug everywhere” that’s still relevant.

I’m not a big VM fan in general, any more.  Ruby cured me of that, although I’ve been writing Java code long enough to have had my share of problems with the JVM, too (not recently; it has been pretty stable for years).  Haskell programs that I wrote 7 years ago still compile and run, and unless there’s a major API change in libc or libm, compiled programs continue to run reliably as well.  This means that I can write a service, get it working the way I want, run it, and forget about it.  It just runs forever, or until it crashes and gets restarted.  Ruby applications, on the other hand, crash almost every time the Ruby VM gets upgraded, and this (IME) happens quite often, and unfortunately usually as part of a larger, system-wide upgrade.  So the admin does a ‘do-release-upgrade’, the Ruby VM gets upgraded, and suddenly some Ruby service that I wrote a couple of years ago stops (silently) working.  This doesn’t happen with my Haskell apps; it doesn’t happen to most natively compiled apps (or it happens much more rarely).  It’s a matter of trust.  I trust natively compiled code more than VM code.

There’s also a matter of ability to distribute code.  With any VM (including the JVM), you have to be careful of the versions.  At one company I previously worked at, we specified support for specific JVM builds, and upgrading the JVM version was fairly expensive, as QA had to run through much more comprehensive tests when the JVM changed.  Because or product included several apps being developed by several different teams, we had to upgrade JVM versions in lock-step.  And if you’re writing an application that’s intended to run on several nodes in a network, you have to make sure that your VMs are all in sync, version-wise.  Erlang was especially bad about this.

Finally, VMs add another layer of failure.  Not only can your hardware, OS, and libraries fail, but now you’ve got a VM that can contain errors and fail.  Higher levels also seem to be more prone to bugs and failures, probably due to the increased complexity of the software.  And the JVM is very complex.

This is why languages like Go and Haskell interest me.  They’re faster than (most) VM code, they’re higher level than C, and they compile to native binaries and so don’t have a dependency on a VM.  I can compile a Haskell or Go program on Ubuntu Linux and scp it over to a RHEL machine and run it.  Basically, as long as the architecture and platform is the same, you have a lot of leeway with binary portability.  There’s a lot of power in that, and it greatly simplifies network administration.  And there’s value in staying on the good side of  network admins.

Copyright © Sean Elliott Russell

comments powered by Disqus