However, that's not quite the same thing. The JVM allows you to change instructions, but not -data-. That is, in between versions you change what data a class contains, there is no way to change it out from the running instance. The JVM either has one version of the bytecode loaded, or the other; it has no concept of transitioning between them.
The BEAM has a mechanism to do that. It can have both loaded. And you can write transformation functions to allow the internal process state to transform from one to the other.
Per the article, "Hot code loading means that the application logic can be updated by changing the runnable code in the system whilst retaining the internal process state" - emphasis added. That's the key bit for maintaining uptime during an upgrade. Honestly, I don't think it's used that often, but it's there.
Erlang, due to how actors encapsulate state, and dynamic typing, allows you to do those things pretty trivially.
I want to make sure you're talking about the same thing.
To be clear: JVM enables the feature, so “technically” JVM allows hot code reload. Not sure how useful this is in practice for non-Clojure JVM users.
Debuggers also use the functionality to allow live code editing and expression evaluation when paused on a breakpoint
[1] https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoa...
There are limits to how much you can change in existing loaded class code, but if you are just loading up new dynamically generated code you can do pretty much anything.
Its a big reason why some of these Java frameworks are so fast. They can generate highly optimized code on the fly, load it, and have it running alongside the existing app code within a few hundred milliseconds. And the Java JIT will optimize it as if the code was there the whole time.
This makes performance optimizations easy that would be impossible in AOT languages like Go, C, C++, Rust, etc