通过对比静态调用与类反射调用方法的效率,来了解现代框架中大量应用的反射调用对性能的影响程度。以便在系统架构中对性能与开发便利性之间进行权衡与取舍。

 

代码1:

/**  
 *  
 */  
package test;   
  
import java.lang.reflect.Method;   
  
/**  
 * @author jetlong  
 *  
 */  
public class PerformanceTest {   
  
    /**  
     * @param args  
     */  
    public static void main(String[] args) throws Exception {   
        int testTime = 10000000;   
        PerformanceTest test = new PerformanceTest();   
        String msg = "this is test message";   
        long bTime = System.currentTimeMillis();   
        for(int i=0; i<testTime; i++) {   
            test.takeAction(msg);   
        }   
        long eTime = System.currentTimeMillis();   
        System.out.println(eTime - bTime);   
           
        Method method = test.getClass().getMethod("takeAction", String.class);   
           
        bTime = System.currentTimeMillis();   
        for(int i=0; i<testTime; i++) {   
            method.invoke(test, msg);   
        }   
        eTime = System.currentTimeMillis();   
        System.out.println(eTime - bTime);   
           
  
    }   
       
    public int takeAction(String msg) {   
        return (msg.length() * (int)(System.currentTimeMillis() % 100000));   
    }   
  
}

 

上述方法有缓存方法
在上述1千万次的测试情况下,直接调用与反射调用的相差一倍左右。
764
1516

819
1466

881
1505

 

其实这种测试结果与测试方法有关,在性能评估中,应评估反射浪费的性能所占的比例,如果是复杂方法,也许这个比例就较低。
如果使用较为简单的调用方法测试(比如单位时钟周期可以完成的操作),则反射所消耗的时间比例是惊人的。


代码2:(不缓存查询到的被调方法)

/**  
 *  
 */  
package test;   
  
import java.lang.reflect.Method;   
  
/**  
 * @author jetlong  
 *  
 */  
public class PerformanceTest {   
  
    /**  
     * @param args  
     */  
    public static void main(String[] args) throws Exception {   
        int testTime = 10000000;   
        PerformanceTest test = new PerformanceTest();   
        String msg = "this is test message";   
        long bTime = System.currentTimeMillis();   
        for(int i=0; i<testTime; i++) {   
            test.takeAction(msg);   
        }   
        long eTime = System.currentTimeMillis();   
        System.out.println(eTime - bTime);   
           
        bTime = System.currentTimeMillis();   
        for(int i=0; i<testTime; i++) {   
            Method method = test.getClass().getMethod("takeAction", String.class);   
            method.invoke(test, msg);   
        }   
        eTime = System.currentTimeMillis();   
        System.out.println(eTime - bTime);   
           
  
    }   
       
    public int takeAction(String msg) {   
        return (msg.length() * (int)(System.currentTimeMillis() % 100000));   
    }   
  
}

 

测试结果:
930
5369

898
5052

885
5346

889
4992

对比代码1的测试结果,没有缓存被调用方法的情况下,性能损失更为严重。
而考虑在框架级实现中,缓存是通过map等机制进行的,那么在获取缓存时的时间成本也要计算在内,则整体性能对比将在上述两个测试之间。


考虑目前的计算机系统的速度,应用开发已经不在那么介意性能,而更为注重系统的可维护性和扩展性以及快速开发效率上。上述的测试结果是在一千万次的测试基础上产生的。而在通常的一次业务请求中,反射使用的次数应该是非常少的,只在框架级基础上被使用,在一个高负载的系统中,业务处理的性能将是关键点,而不在于使用的这些反射所带来的性能影响上。而使用反射所带来的开发便利与可维护性可扩展性的提升所带来的价值,是远远高于其所损耗的性能的。

又回想起原来在某个所谓高性能项目中通过减少反射来提高性能的做法,现在想来,比较愚蠢。这说明前期的测试工作没有到位,而带来这样的结论偏差,从而导致了开发与维护的不便,而且极大地影响了开发速度。
其实那个系统的大部分性能瓶颈都是在数据库上,大部分的业务处理都是在数据库中进行的,在项目后面的性能测试中发现,WEB服务器的负载非常低,远远低于数据库,大部分的操作都是在等待数据库的返回。
前期某些推论既没有经过验证,也没有相关的使用经验来支持此推论,是导致这种错误的根源。在将来的架构设计工作与框架选型要加强这方面的评估工作,来达到性能与开发效率间的最佳平衡。