Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

Why HotSwap wasn’t good enough in 2001…and still isn’t today

Welcome back to our deep-dive into how JRebel works to solve a bunch of issues intrinsic in the nature of Java. If you remember in the first blog post of this series, we sat down with our boss Jevgeni Kabanov, the mad scientist behind the creation of JRebel, to take a look at the problem that exists in Java which has always existed since its creation over 20 years ago–long reload times of class changes. In this post, we’ll take a look at the tools at our disposal to overcome this problem looking at how they work under the covers, including HotSwap, which, introduced in 2001, was barely beneficial to handle the plethora of code changes that developers made back then…and why JRebel was created to pick up the rest of the slack.

The HotSwap Solution

HotSwap goes about a reload by redefining the existing Java class that has been loaded. This means handling updates, such as to instanceKlass in Figure 1, and other aspects including the virtual method table (vtable) and interface method table (itable). You’ll see from the HotSpot class layout, Figure 1, that this will get tricky quickly, as there will be specific solutions for different JVM implementations.

Screen Shot 2014-03-06 at 10.23.06.png

Figure 1. HotSpot Class Layout

Also, different garbage collectors and heap layouts, heap fragmentation rules will also have implications on how redefining a class should be done. This is getting complex extremely quickly.

How JRebel is like HotSwap made by Walter White

Like the main character in Breaking Bad, JRebel is different from any productivity of its kind. It instruments the application and JVM classes to create a layer of indirection. In the case an application class is loaded, all method bodies will have a redirection using the runtime redirection service, as shown in Figure 2. This service manages and loads the class and method versions using anonymous inner classes created for each version that is reloaded. Let’s look at an example. We’ll create a new class C with two methods:

public class C extends X {
 int y = 5;
 int method1(int x) {
   return x + y;
 }
 void method2(String s) {
   System.out.println(s);
 }
}

When Class C is loaded for the first time, JRebel instruments the class. The signature of this class will be the same, but the method bodies are now being redirected. The loaded class will now look something like this:

public class C extends X {
 int y = 5;
 int method1(int x) {
   Object[] o = new Object[1];
   o[0] = x;
   return Runtime.redirect(this, o, "C", "method1", "(I)I");
 }
 void method2(String s) {
   Object[] o = new Object[1];
   o[0] = s;
   return Runtime.redirect(this, o, "C", "method2", "(Ljava/lang/String;)V");
 }
}

To the redirect calls, we passing in the calling object, the parameters to the method that has been called, our class name, our method name and the types of the parameters and return. JRebel also loads a class with the implementations at a specific version, initially version 0. Let’s see what that looks like:

public abstract class C0 {
 public static int method1(C c, int x) {
   int tmp1 = Runtime.getFieldValue(c, "C", "y", "I");
   return x + tmp1;
 }
 public static void method2(C c, String s) {
   PrintStream tmp1 =
     Runtime.getFieldValue(
       null, "java/lang/System", "out", "Ljava/io/PrintStream;");
   Object[] o = new Object[1];
   o[0] = s;
   Runtime.redirect(tmp1, o, "java/io/PrintStream;", "println","(Ljava/lang/String;)V");
 }
}

Let’s now say the user changes their class C by adding a new method z() and invoking it from method1. Class C now looks like this:

public class C {
 int y = 5;
 int z() {
   return 10;
 }
 int method1(int x) {
   return x + y + z();
 }
 ...
}

The next time the runtimes uses this class, JRebel detects there is a newer version that has been compiled and on the filesystem, so it loads the new version, C1. This version has the additional method z and the updated implementation for method1.

public class C1 {
 public static int z(C c) {
   return 10;
 }
 public static int method1(C c, int x) {
   int tmp1 = Runtime.getFieldValue(c, "C", "y", "I");
   int tmp2 = Runtime.redirect(c, null, "C", "z", "(V)I");
   return x + tmp1 + tmp2;
 }
 ...
}

The Runtime.redirect call will always be routed to the latest version of the class C, so calling new C().method1(10) would return 15 before the code change and 25 afterwards. This implementation misses a lot of detail and optimizations, but you get the idea.

Final thoughts

Without HotSwap’s significant limitations on the types of class changes developers frequently need to reload dynamically, it’s possible that JRebel would never have been created. This article shows you a bit of what JRebel does behind the scenes to speed up things a bit for modern Java EE development, where things can get very complicated–especially when you want to extend your stack outside of Java EE and bring in some additional libraries/frameworks. This is what we get into next time, how to make it all work with 50 or so frameworks and technologies that certainly don’t play nice right out of the box. Got comments? Please leave below or tweet @sjmaple! :-)


If you’d like to see a bit more about what JRebel is capable of, join us on these product pages and click this button here:

MORE ABOUT JREBEL