Java is the perfect language with an awful runtime
By Daniel Samson · 2026-04-23
Java is, genuinely, one of the best languages to write in today. It's also saddled with a runtime that makes it a pain to deploy in the world we actually ship into. Both things are true, and the gap between them is maddening.
The language quietly got brilliant
If your mental image of Java is AbstractSingletonProxyFactoryBean and fifty lines of ceremony to read a file, you've not looked in a decade. Records, sealed types, pattern matching, switch expressions, var, and virtual threads have turned it into a genuinely modern, expressive language. It's statically typed, refactors like a dream, and the compiler catches the things you want caught.
The ecosystem is unmatched
Decades of battle-tested libraries. The best IDE tooling in the industry. Profilers and observability that put most ecosystems to shame. Whatever you need to do, someone solved it properly in Java fifteen years ago and has been hardening it ever since.
Then you try to ship it
And the JVM presents the bill. A multi-hundred-megabyte base image. A cold start measured in seconds. A baseline memory appetite that makes you wince when you're paying per megabyte and packing containers onto nodes. In a world of Kubernetes density, autoscaling, and serverless cold starts, the runtime's footprint is a tax on everything Java is good at.
GraalVM is the asterisk, not the answer
Yes, GraalVM native image fixes the startup and footprint — and in exchange it breaks reflection-heavy libraries (i.e. half the ecosystem), lengthens your build, and adds a configuration dance for anything dynamic. It's a remarkable workaround. It is not the JVM growing up.
Fix the runtime's footprint and startup without sacrificing the ecosystem, and Java eats everyone's lunch again. The language already earned it. The runtime keeps it stuck in the last era.