Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JRebel Under the Covers

Simon Maple
September 16, 2015

JRebel Under the Covers

This talk presents a number of conceptual and technical challenges that we discovered while building JRebel. JRebel performs class reloading and framework integrations which the JVM doesn't provide out the box. This session demystifies the magic.
This talk presents a number of conceptual and technical challenges that we discovered while building JRebel. At first, the JVM wasn't designed for live updates, so we will talk about the engine that mitigates the problem. Secondly, the diversity of Java ecosystem, created by the variety of application servers, the frameworks and tools, makes it challenging in creating a generic solution that would fit the majority of developers. We will see, how Java platform itself allows us to develop a solution by applying bytecode instrumentation mechanism.
JRebel does live code reloading to ensure that the developer can keep instantly alternating between the developing environment and the web browser, to save wasted time and increase the productivity flow.

Simon Maple

September 16, 2015
Tweet

More Decks by Simon Maple

Other Decks in Education

Transcript

  1. Classes Libraries OldClassLoader NewClassLoader Sevlet New Classes New Libraries Sevlet

    Session Session init() App State App State Serialize/deserialize
  2. Class loaders are good for isolation, but suck at reloading

    the code … (and perfect for memory leaks)
  3. M. Dmitriev. Safe class and data evolution in large and

    long-lived java[tm] applications. Technical report, Mountain View, CA, 2001.
  4. HotSwap   JRebel   Changing  method  bodies   Adding/removing  methods

      Adding/removing  constructors   Adding/removing  fields   Adding/removing  annotaBons   Changing  superclass   Adding/removing  interfaces  
  5. ClassA +field1 +field2 +field3 +method1(n:int) +method2(s:String) +method3(z:double) ClassA +field1 +field2

    +field3 +proxy methods ClassA0 +method1(n:int) +method2(s:String) +method3(z:double) Transformer
  6. public class C extends X { int y = 5;

    int method1(int x) { return x + y; } void method2(String s) { System.out.println(s); } }
  7. 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"); } }
  8. 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"); } }
  9. public class C extends X { int y = 5;

    int method1(int x) { return x + y; } //... } public class C extends X { int y = 5; int z() { return 10; } int method1(int x) { return x + y + z(); } //... }
  10. 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; } //... }
  11. static { } Adding new static fields Removing static fields

    Changing values of static fields Factories etc
  12. package a; public class A { int say() {return 1;}

    } package a; class Test { public static void main(String args[]) { a.A a = new b.B(); System.out.println(a.say()); } } package b; public class B extends a.A { int say() {return 2;} } An Exercise
  13. import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; public class Agent { public static

    void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { }); } } java  –javaagent:agent.jar  …   META-INF/MANIFEST.MF Premain-Class: Agent
  14. new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined,

    ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); } }
  15. new ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined,

    ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); } }
  16. @Path(“/”) public String foo() { return “Foo”; } @Path(“/foobar”) public

    String foo() { return “FooBar”; } JRebel   core   JRebel   Spring   plugin  
  17. http://zeroturnaround.com/company/case-studies/ “Team velocity was increased by 40.6%, and according to

    t-test it was a statistically significant difference.” “Netty and Spring together with JRebel is real pleasure to work with. The value of time saved is over 50% of my development time.”