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

Anton Arhipov

April 28, 2017
Tweet

More Decks by Anton Arhipov

Other Decks in Technology

Transcript

  1. 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
  2. Dynamic Code Evolution for Java T. Würthinger, C. Wimmer, L.

    Stadler. 2010 Statements Methods Fields Hierarchy + + + x x x Binary-compatible Binary-incompatible
  3. 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; }
  4. 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; }
  5. 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; }
  6. 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; }
  7. 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; }
  8. public static class Context { public HobbyService hobbyService = new

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

    HobbyService(); public AnyService anyService = new AnyService(); public void init() { hobbyService.user = new User(); anyService.initialize() } }
  10. 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); }
  11. 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
  12. 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
  13. public class C extends X { int y = 5;

    int method1(int x) { return x + y; } void method2(String s) { System.out.println(s); } }
  14. 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"); } }
  15. 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"); } }
  16. 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"); } }
  17. 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; }
  18. 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"); } }
  19. 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(); } //... }
  20. 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; } //... }