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

The hitchhiker's guide to Java class reloading

The hitchhiker's guide to Java class reloading

1bc80e2eee2adeaa8bb577798d92e9d0?s=128

Anton Arhipov

April 28, 2017
Tweet

Transcript

  1. The hitchhiker’s guide to Java class reloading Oracle Code Prague

    2017 @antonarhipov
  2. whoami Anton Arhipov @antonarhipov

  3. None
  4. None
  5. https://zeroturnaround.com/rebellabs/java-ee-productivity-report-2011/ 10% 20% 30% 0.5 1 2 3 4 5

    6 7 8 10+ minutes
  6. https://zeroturnaround.com/rebellabs/java-ee-productivity-report-2011/ 10% 20% 30% 0.5 1 2 3 4 5

    6 7 8 10+ The only effective developers?? minutes
  7. https://zeroturnaround.com/rebellabs/java-ee-productivity-report-2011/ 10% 20% 30% 0.5 1 2 3 4 5

    6 7 8 10+ How about those? minutes
  8. HotSwap Class loaders Java agents & instrumentation Challenges

  9. hotswap EST. 2001

  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. None
  17. HOW ABOUT… refactoring?

  18. None
  19. None
  20. instanceKlass constantPoolOop constants() constantPoolCacheOop cache() pool_holder() klassVTable Embedded klassITable Embedded

    Embedded statics nmethod code() method() constants() objArrayOop methodOop methods() M. Dmitriev. Safe class and data evolution in large and long-lived Java (тм) applications. Technical report, Mountain View. 2001
  21. What if… hotswap++

  22. Dynamic Code Evolution for Java T. Würthinger, C. Wimmer, L.

    Stadler. 2010 Statements Methods Fields Hierarchy + + + x x x Binary-compatible Binary-incompatible
  23. None
  24. Classloaders

  25. Class<?> uc1 = User.class; Class<?> uc2 = new DynamicClassLoader().load("com.zt.User"); out.println(uc1.getName());

    // com.zt.User out.println(uc2.getName()); // com.zt.User out.println(uc1.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2 out.println(uc2.getClassLoader()); // com.zt.DynamicClassLoader@22b4bba7 User.age = 11; out.println((int) ReflectUtil.getStaticFieldValue("age", uc1)); // 11 out.println((int) ReflectUtil.getStaticFieldValue("age", uc2)); // 10 public class User { public static int age = 10; }
  26. Class<?> uc1 = User.class; Class<?> uc2 = new DynamicClassLoader().load("com.zt.User"); out.println(uc1.getName());

    // com.zt.User out.println(uc2.getName()); // com.zt.User out.println(uc1.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2 out.println(uc2.getClassLoader()); // com.zt.DynamicClassLoader@22b4bba7 User.age = 11; out.println((int) ReflectUtil.getStaticFieldValue("age", uc1)); // 11 out.println((int) ReflectUtil.getStaticFieldValue("age", uc2)); // 10 public class User { public static int age = 10; }
  27. Class<?> uc1 = User.class; Class<?> uc2 = new DynamicClassLoader().load("com.zt.User"); out.println(uc1.getName());

    // com.zt.User out.println(uc2.getName()); // com.zt.User out.println(uc1.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2 out.println(uc2.getClassLoader()); // com.zt.DynamicClassLoader@22b4bba7 User.age = 11; out.println((int) ReflectUtil.getStaticFieldValue("age", uc1)); // 11 out.println((int) ReflectUtil.getStaticFieldValue("age", uc2)); // 10 public class User { public static int age = 10; }
  28. Class<?> uc1 = User.class; Class<?> uc2 = new DynamicClassLoader().load("com.zt.User"); out.println(uc1.getName());

    // com.zt.User out.println(uc2.getName()); // com.zt.User out.println(uc1.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2 out.println(uc2.getClassLoader()); // com.zt.DynamicClassLoader@22b4bba7 User.age = 11; out.println((int) ReflectUtil.getStaticFieldValue("age", uc1)); // 11 out.println((int) ReflectUtil.getStaticFieldValue("age", uc2)); // 10 public class User { public static int age = 10; }
  29. Class<?> uc1 = User.class; Class<?> uc2 = new DynamicClassLoader().load("com.zt.User"); out.println(uc1.getName());

    // com.zt.User out.println(uc2.getName()); // com.zt.User out.println(uc1.getClassLoader()); // sun.misc.Launcher$AppClassLoader@18b4aac2 out.println(uc2.getClassLoader()); // com.zt.DynamicClassLoader@22b4bba7 User.age = 11; out.println((int) ReflectUtil.getStaticFieldValue("age", uc1)); // 11 out.println((int) ReflectUtil.getStaticFieldValue("age", uc2)); // 10 public class User { public static int age = 10; }
  30. while(true) { Class<?> uc = new DynamicClassLoader().load("com.zt.User"); ReflectUtil.invokeStatic("getHobby", uc); }

    public class User { public Hobby getHobby() { return Basketball(); } }
  31. while(true) { Class<?> uc = new DynamicClassLoader().load("com.zt.User"); ReflectUtil.invokeStatic("getHobby", uc); }

    public class User { public Hobby getHobby() { return Basketball(); } }
  32. public static class Context { public HobbyService hobbyService = new

    HobbyService(); public void init() { hobbyService.user = new User(); anyService.initialize() } }
  33. public static class Context { public HobbyService hobbyService = new

    HobbyService(); public AnyService anyService = new AnyService(); public void init() { hobbyService.user = new User(); anyService.initialize() } }
  34. public static class Context { public HobbyService hobbyService = new

    HobbyService(); public AnyService anyService = new AnyService(); public void init() { hobbyService.user = new User(); anyService.initialize() } } while(true) { Class<?> c = new DynamicClassLoader().load("com.zt.Context"); Object context = c.newInstance(); ReflectUtil.invokeMethod("init", context); invokeService(context); }
  35. DynamicClassLoader Context class HobbyService class User class Context object HobbyService

    object User object Reloadable “region” Live thread
  36. None
  37. None
  38. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> DEMO

  39. None
  40. None
  41. None
  42. None
  43. 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 { // here be dragons }); } } $> java –javaagent:agent.jar application.Main META-INF/MANIFEST.MF Premain-Class: Agent
  44. ClassFileTransformer ClassA ClassA ClassA0 +field1 +field2 +field3 +method1 +method2 +method3

    +method1 +method2 +method3 +field1 +field2 +field3 +proxy_methods A thousand years of productivity: the JRebel story E. Kabanov, V. Vene, 2012
  45. public class C extends X { int y = 5;

    int method1(int x) { return x + y; } void method2(String s) { System.out.println(s); } }
  46. 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"); } }
  47. 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"); } }
  48. 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"); } }
  49. 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"); } } int method1(int x) { return x + y; }
  50. 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"); } }
  51. 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(); } //... }
  52. 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; } //... }
  53. WHAT ABOUT… configuration?

  54. DEMO

  55. HotSwap Class loaders Java agents & instrumentation

  56. anton@zeroturnaround.com @antonarhipov