How does JRebel work?

Great article on this topic by Dave Booth. Reloading Java Classes: HotSwap and JRebel — Behind the Scenes.


This is the closest reasoning I have read on how JRebel works by Simon, ZT Technical Evangelist.

Pasting the contents here:


Jrebel 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.

Source: http://zeroturnaround.com/rebellabs/why-hotswap-wasnt-good-enough-in-2001-and-still-isnt-today/


JRebel uses class rewriting (both ASM and Javassist) and JVM integration to version individual classes. Plus it integrates with app servers to redirect class/resource and web server lookups back to the workspace. And it also integrates with most app servers and frameworks to propagate changes to the configuration (metadata or files). That's the short of it. The long of it takes 10 world-class engineers to develop and support and is our commercial secret :)

Tags:

Jrebel