打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
阿里巴巴技术门户
反射的性能到底怎样?
发表于:2007-5-17 下午5:21
 
回复
image001.gif (22.6 K)
反射的性能.doc (131.5 K)

最近写框架,不可避免的碰到反射的问题。一直以来认为反射是相对比较慢的,干脆写个程序测试对比一下不同的反射方案及性能。

对于一个简单的对象:

publicstaticclass TestObject {

   private Strings;

   public String getS() {

       returns;

    }

   publicvoid setS(String s) {

       this.s = s;

    }

}

我比较了四种方案:

1.        直接存取

o.setS("123");

o.getS();

这当然是最快的方法。

2.        通过反射存取

Methodsetter =getMethod(TestObject.class,"setS",new Class[] { String.class },false);

Methodgetter =getMethod(TestObject.class,"getS",new Class[] {},false);

setter.invoke(o,new Object[] {"123" });

getter.invoke(o,new Object[0]);

这应该是相对最慢的方法。但究竟慢多少,一会我们再看。

3.        利用CGLIB FastClass存取

CGLIB提供了一个简单的FastClass机制,据称比Java反射快上许多倍,咱们试一下看看。

FastClassfc = FastClass.create(TestObject.class);

FastMethodsetter =fc.getMethod("setS",new Class[] { String.class });

FastMethodgetter =fc.getMethod("getS",new Class[] {});

setter.invoke(o,new Object[] {"123" });

getter.invoke(o,new Object[0]);

除了类名不同以外,其余操作和JDK自带的反射API非常相似。

如果你运行并跟踪一下,很容易了解这种FastClass的机制是怎样工作的。CGLIB在运行时生成一个类,把反射的调用转换成直接调用。通过跟踪代码,很容易得到自动生成的二进制类,并用JAD反编译可得类似下面的代码:

publicclass FastMethod {

   privatefinal FastClassowningClass;

   privatefinalintmethodIndex;

   public FastMethod(FastClass fc,int index) {

       this.owningClass = fc;

       this.methodIndex = index;

    }

   public Object invoke(Object obj, Object[] args)throws InvocationTargetException {

       returnowningClass.invoke(methodIndex, obj, args);

    }

}

class GeneratedFastClass_TestObjectextends FastClass {

   public Object invoke(int methodIndex, Object o, Object[] params) {

       switch (methodIndex) {

           case METHOD_SET_S:

                ((TestObject) o).setS((String) params[0]);

               break;

           case ...

        }

    }

}

使用这种方法的唯一代价就是:需要为每个类生成一个FastClass的实现。

4.        通过反射存取,但避开Java安全检查

Methodsetter =getMethod(TestObject.class,"setS",new Class[] { String.class },false);

Methodgetter =getMethod(TestObject.class,"getS",new Class[] {},false);

setter.setAccessible(true);

getter.setAccessible(true);

setter.invoke(o,new Object[] {"123" });

getter.invoke(o,new Object[0]);

这是我从网上找到的技巧。

现在让我们就这四种方案,来测试一下性能。测试的方法很简单:循环1000万遍,比较时间。完整的测试程序如下:

import java.lang.reflect.Method;

import java.text.MessageFormat;

import net.sf.cglib.reflect.FastClass;

import net.sf.cglib.reflect.FastMethod;

publicabstractclass ReflectionPerformanceTest {

   publicstaticvoid main(String[] args)throws Exception {

       int max = 10000000;

       directAccess.run("Direct access", max);

       fastClass.run("CGLIB Fast Class", max);

       reflection.run("JDK reflection", max);

       reflectionWithoutSecurityCheck.run(

               "JDK reflection without security check", max);

    }

   privatestatic Method getMethod(Class clazz, String name,

            Class[] paramTypes,boolean noSecurityCheck) {

        Method method;

       try {

            method = clazz.getMethod(name, paramTypes);

        }catch (Exception e) {

           thrownew IllegalArgumentException(e);

        }

       if (noSecurityCheck) {

            method.setAccessible(true);

        }

       return method;

    }

   privatestatic ReflectionPerformanceTestdirectAccess =new ReflectionPerformanceTest() {

       protectedvoid runOnce(TestObject o)throws Exception {

            o.setS("123");

            o.getS();

        }

    };

   privatestatic ReflectionPerformanceTestfastClass =new ReflectionPerformanceTest() {

       private FastClassfc = FastClass.create(TestObject.class);

       private FastMethodsetter =fc.getMethod("setS",

               new Class[] { String.class });

       private FastMethodgetter =fc.getMethod("getS",new Class[] {});

       protectedvoid runOnce(TestObject o)throws Exception {

           setter.invoke(o,new Object[] {"123" });

           getter.invoke(o,new Object[0]);

        }

    };

   privatestatic ReflectionPerformanceTestreflection =new ReflectionPerformanceTest() {

       private Methodsetter =getMethod(TestObject.class,"setS",

               new Class[] { String.class },false);

       private Methodgetter =getMethod(TestObject.class,"getS",

               new Class[] {},false);

       protectedvoid runOnce(TestObject o)throws Exception {

           setter.invoke(o,new Object[] {"123" });

           getter.invoke(o,new Object[0]);

        }

    };

   privatestatic ReflectionPerformanceTestreflectionWithoutSecurityCheck

                                                              =new ReflectionPerformanceTest() {

       private Methodsetter =getMethod(TestObject.class,"setS",

               new Class[] { String.class },true);

       private Methodgetter =getMethod(TestObject.class,"getS",

               new Class[] {},true);

       protectedvoid runOnce(TestObject o)throws Exception {

           setter.invoke(o,new Object[] {"123" });

           getter.invoke(o,new Object[0]);

        }

    };

   publicvoid run(String desc,int max)throws Exception {

        TestObject o =new TestObject();

       long start = System.currentTimeMillis();

       for (int i = 0; i < max; i++) {

            runOnce(o);

        }

        System.out.println(MessageFormat.format(

               "duration={1,number,####,###} ms - {0}",new Object[] { desc,

                       new Long(System.currentTimeMillis() - start) }));

    }

   protectedabstractvoid runOnce(TestObject o)throws Exception;

   publicstaticclass TestObject {

       private Strings;

       public String getS() {

           returns;

        }

       publicvoid setS(String s) {

           this.s = s;

        }

    }

}

下面是在不同的JDK下的测试结果:(我的机器是Core 2 Dual 2.66MHz

JDK 1.4.2

duration=122 ms - Direct access

duration=936 ms - CGLIB Fast Class

duration=3,809 ms - JDK reflection

duration=1,605 ms - JDK reflection without security check

JDK 1.5.0

duration=83 ms - Direct access

duration=655 ms - CGLIB Fast Class

duration=3,150 ms - JDK reflection

duration=896 ms - JDK reflection without security check

JDK 1.6.0

duration=113 ms - Direct access

duration=562 ms - CGLIB Fast Class

duration=2,384 ms - JDK reflection

duration=743 ms - JDK reflection without security check

上述数据画成图表更直观:(比较矮的是比较好的

 

观察:

l 新一代的JDK比上一代JDK性能提升非常明显,只是JDK 1.6.0在直接访问时性能反常地输给JDK 1.5.0

l 直接访问在性能上占有绝对的优势,约比Java反射快上20JDK 1.6.0)到30倍(JDK 1.4.2)左右。

l CGLIB FastClass的效果还是不错的,比直接访问慢5JDK1.6.0)到10倍(JDK 1.4.2),比Java反射快4倍左右。

l 另人惊异的是,如果把“安全检查”关掉,再调用Java反射,其结果和CGLIB FastClass极其接近,比普通的Java反射快34倍左右。没想到Java反射居然把大量时间用在这里了。

结论:

l Java反射比直接访问要慢20倍,但别忘了我们测试了1000万次。实际单次访问的绝对时间差别,还是极其微小的。

l CGLib FastClass的效果不错,然而你必须附带CGLIB这个类库,而且在运行时要承担动态生成大量类的时间和空间。

l 把“安全检查”关掉,再用正常的方法调用Java反射,可以取得和CGLIB FastClass极为接近的效果,但却少了CGLIB的代价。因此如果security策略许可的话,这是最好的方案。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
检查字体设置
【原创】Java类加载原理解析 - Live a simple life - BlogJa...
关于getClass().getClassLoader()
Java 8新特性探究(八)精简的JRE详解
cglib源码学习交流
Java 8 中 HashMap 的性能提升
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服