7 Replies Latest reply: Mar 11, 2006 8:24 PM by chung chung RSS

On a faster java.lang.reflect.Method using Javassist

Adrian Brock Master

I've been playing with generating our own reflection objects
rather than using the JDK classes.

My Javassist reflection methods are significantly faster

 int iterations = 10000000;

 public void testStressJavassist() throws Throwable
 {
 ClassInfo typeInfo = getClassInfo(String.class);
 MethodInfo methodInfo = typeInfo.getDeclaredMethod("toString", null);
 String hello = "Hello";
 long start = System.currentTimeMillis();
 for (int i = 0; i < iterations; ++i)
 methodInfo.invoke(hello, null);
 long end = System.currentTimeMillis();
 System.out.println("testStressJavassist " + (end - start) + "ms");
 }

 public void testStressReflect() throws Throwable
 {
 Method method = String.class.getDeclaredMethod("toString", null);
 String hello = "Hello";
 long start = System.currentTimeMillis();
 for (int i = 0; i < iterations; ++i)
 method.invoke(hello, null);
 long end = System.currentTimeMillis();
 System.out.println("testStressReflect " + (end - start) + "ms");
 }

 public void testStressJavassist2() throws Throwable
 {
 ClassInfo typeInfo = getClassInfo(String.class);
 TypeInfo intInfo = getTypeInfo(Integer.TYPE);
 MethodInfo methodInfo = typeInfo.getDeclaredMethod("charAt", new TypeInfo[] { intInfo });
 String hello = "Hello";
 Object[] params = { new Integer(4) };
 long start = System.currentTimeMillis();
 for (int i = 0; i < iterations; ++i)
 methodInfo.invoke(hello, params);
 long end = System.currentTimeMillis();
 System.out.println("testStressJavassist2 " + (end - start) + "ms");
 }

 public void testStressReflect2() throws Throwable
 {
 Method method = String.class.getDeclaredMethod("charAt", new Class[] { Integer.TYPE });
 String hello = "Hello";
 Object[] params = { new Integer(4) };
 long start = System.currentTimeMillis();
 for (int i = 0; i < iterations; ++i)
 method.invoke(hello, params);
 long end = System.currentTimeMillis();
 System.out.println("testStressReflect2 " + (end - start) + "ms");
 }


Output - you can also see the cost of Autoboxing the int ;-)
testStressJavassist 479ms
testStressReflect 2937ms
testStressJavassist2 1153ms
testStressReflect2 3344ms


Rather than showing the code used to generate the class
here is the generated code:
java.lang.String.toString();

public Object invoke(Object target, Object[] args) throws Throwable {if (target == null) throw new IllegalArgumentException("Null target for
 toString()Ljava/lang/String;");if (target instanceof java.lang.String == false) throw new IllegalArgumentException("Target " + target + " i
s not an instance of java.lang.String for toString()Ljava/lang/String;");if (args != null && args.length != 0)throw new IllegalArgumentExcep
tion("Expected no parameters for toString()Ljava/lang/String;");return ((java.lang.String)target).toString();}

java.lang.String.charAt(int);

public Object invoke(Object target, Object[] args) throws Throwable {if (target == null) throw new IllegalArgumentException("Null target for
 charAt(I)C");if (target instanceof java.lang.String == false) throw new IllegalArgumentException("Target " + target + " is not an instance
of java.lang.String for charAt(I)C");if (args == null || args.length != 1) throw new IllegalArgumentException("Expected 1 parameter(s) for c
harAt(I)C");if (args[0] == null) throw new IllegalArgumentException("Parameter 0 cannot be null for int for charAt(I)C");if (args[0] != null
 && args[0] instanceof java.lang.Integer == false) throw new IllegalArgumentException("Parameter 0 is not an instance of java.lang.Integer f
or charAt(I)C");return new java.lang.Character(((java.lang.String)target).charAt(((java.lang.Integer)args[0]).intValue()));}


I also have an option for turning off parameter checking,
but this doen't seem to make any measurable difference to the numbers
although the generated code is easier to understand :-)
String.toString();

public Object invoke(Object target, Object[] args) throws Throwable {return ((java.lang.String)target).toString();}

String.charAt(int);

public Object invoke(Object target, Object[] args) throws Throwable {return new java.lang.Character(((java.lang.String)target).charAt(((java
.lang.Integer)args[0]).intValue()));}


  • 1. Re: On a faster java.lang.reflect.Method using Javassist
    Adrian Brock Master

    The only downside to this approach is support for

    AccessibleObject.setAccesible()
    


    But this is possible if class verfication is turned off (e.g. JRockit has this option).
    Or in Sun's JDK, you just make the class extend "MagicAccessorImpl" instead
    of Object.
    http://www.jroller.com/page/slava?entry=the_hotspot_source_code_is
    Which is like a per class flag to turn of class verification. :-)

    If such approaches fail, we can always revert back to reflection
    for the few use cases where this is needed.

  • 2. Re: On a faster java.lang.reflect.Method using Javassist
    Max Rydahl Andersen Master

    What is the difference between this and what have recently been added to javaassist to allow it be used on equal foot as cglib ?



  • 3. Re: On a faster java.lang.reflect.Method using Javassist
    Adrian Brock Master

    It provides an api like java.lang.reflect (e.g. per method)
    rather than a reflective class like cglib.

  • 4. Re: On a faster java.lang.reflect.Method using Javassist
    Max Rydahl Andersen Master

    ok, could be interesting to try and use this api for the hibernate core method reflection and lookup.

  • 5. Re: On a faster java.lang.reflect.Method using Javassist
    chung chung Newbie

    Hi,
    Can help me pls,I try adrian sample but have error.

     int iterations = 1000000;
     public static void main(String[] args) {
     myTest my = new myTest();
     try {
     my.testStressJavassist();
     my.testStressReflect();
     }catch(Throwable ex){
     ex.printStackTrace(System.out);
     }
     }
     public void testStressJavassist() throws Throwable
     {
     ClassInfo typeInfo = getClassInfo(String.class);
     MethodInfo methodInfo = typeInfo.getDeclaredMethod("toString", null);
     String hello = "Hello";
     long start = System.currentTimeMillis();
     for (int i = 0; i < iterations; ++i)
     methodInfo.invoke(hello, null);
     long end = System.currentTimeMillis();
     System.out.println("testStressJavassist " + (end - start) + "ms");
     }
     public void testStressReflect() throws Throwable
     {
     Method method = String.class.getDeclaredMethod("toString", null);
     String hello = "Hello";
     long start = System.currentTimeMillis();
     for (int i = 0; i < iterations; ++i)
     method.invoke(hello, null);
     long end = System.currentTimeMillis();
     System.out.println("testStressReflect " + (end - start) + "ms");
     }
     public ClassInfo getClassInfo(Class clazz){
     TypeInfoFactory typeFactory = new JavassistTypeInfoFactory();;
     return(ClassInfo)typeFactory.getTypeInfo(clazz);
     }
    

    error StackTrace is
    java.lang.RuntimeException:
    Cannot compile public Object invoke(Object target, Object[] args) throws Throwable
    {if (target == null) throw new IllegalArgumentException("Null target for toString()Ljava/lang/String;");
    if (target instanceof java.lang.String == false) throw new IllegalArgumentException("Target " + target + " is not an instance of java.lang.String for toString()Ljava/lang/String;");
    if (args != null && args.length != 0)throw new IllegalArgumentException("Expected no parameters for toString()Ljava/lang/String;");
    return ((java.lang.String)target).toString();}
     at org.jboss.reflect.plugins.javassist.JavassistReflectionFactory.createMethod(JavassistReflectionFactory.java:229)
     at org.jboss.reflect.plugins.javassist.JavassistMethodInfo.invoke(JavassistMethodInfo.java:157)
     at mytest.myTest.testStressJavassist(myTest.java:33)
     at mytest.MyJavassistTest.main(MyJavassistTest.java:15)
    Caused by: javassist.CannotCompileException: [source error] incompatible array types
     at javassist.CtNewMethod.make(CtNewMethod.java:78)
     at javassist.CtNewMethod.make(CtNewMethod.java:44)
     at org.jboss.reflect.plugins.javassist.JavassistReflectionFactory.createMethod(JavassistReflectionFactory.java:224)
     ... 3 more
    

    My config are jvm 1.5 and javassist-3.0

    thank

  • 6. Re: On a faster java.lang.reflect.Method using Javassist
    Adrian Brock Master

    This stuff was tested with javaassist 3.1
    available from jboss binary repository
    http://repository.jboss.com/javassist/3.1/

  • 7. Re: On a faster java.lang.reflect.Method using Javassist
    chung chung Newbie

    Hi,

    Thanks for your help ;-)