您的位置:首页 > 编程语言 > Java开发

java的反射性能测试

2015-08-02 15:28 393 查看
测试反射实现的对象生成、方法调用的性能。在这里被测试的类代码如下所示。

public class TestBean {

	public final static String DEFAULT="default";
	private String word;
	
	public TestBean() {
		word=DEFAULT;
	}
	public String getWord(){
		return word;
	}
	
	public void setWord(String word){
		this.word=word;
	}

}
这个类很简单,提供一个构造函数,一个get和set属性值的方法。再比较直接调用的性能,普通反射的性能,带缓存的反射性能。

首先建立了一个测试框架,对给定的测试类统计用时。代码如下所示。

package reflectTest;

/**简易测试框架,统计运行时间
 * */
public class TestBeanCount{
	public static void testBean(TestBeanRun tbr){
		System.out.printf("%10s\t%5s\t%6s\n","method","time","avr" );
		long[]time=new long[3];
		String[] methods = new String[] { "construct", "get", "set" };
		for (int t = 10; t-- > 0;) {
			tbr.setTestMethod(TestBeanRun.CONSTRUCT);
			time[0] = getUsedTime(tbr);
			tbr.setTestMethod(TestBeanRun.GET);
			time[1] = getUsedTime(tbr);
			tbr.setTestMethod(TestBeanRun.SET);
			time[2] = getUsedTime(tbr);
			for (int k = 0; k < 3; ++k) {
				System.out.printf("%10s\t%5d\t%6.3f\n", methods[k], time[k],
						time[k] * 0.001);
			}
		}
	}
	public static final long DEFAULT_RUN_TIME=100000000L;
	public static long getUsedTime(Runnable run,long runTime){
		long start=System.currentTimeMillis();
		while(runTime-->0){
			run.run();
		}
		return System.currentTimeMillis()-start;
	}
	public static long getUsedTime(Runnable run){
		return getUsedTime(run,DEFAULT_RUN_TIME);
	}
}


这里用到了一个类TestBeanRun,这个类实现了接口Runnable,用以执行方法调用,如对象生成,get/set方法调用。测试框架对这个类的三个类型的方法调用分别执行10^8次,然后统计用时,计算平均时间。

TestBeanRun的代码如下所示。这个类里面用一个标志位来选择执行哪种调用,生成对象,或者get调用,set调用。这里默认的是进行常规的直接调用。普通反射和带缓存反射的测试通过继承这个类,并重写那三个方法。

package reflectTest;

public class TestBeanRun implements Runnable{
	public static final int CONSTRUCT=1;
	public static final int GET=2;
	public static final int SET=3;
	private int testMethod;
	protected String s;
	protected TestBean tb;
	private final static TestBean TB = new TestBean();
	public void setTestMethod(int testMethod){
		this.testMethod=testMethod;
	}
	@Override
	public void run() {
		switch(this.testMethod){
		case CONSTRUCT:
			testConstruct();
			break;
		case GET:
			testGet();
			break;
		case SET:
			testSet();
			break;
		default:
				
		}
	}
	public void testConstruct(){
		tb=new TestBean();
	}
	public void testGet(){
		s=TB.getWord();
	}
	public void testSet(){
		TB.setWord("ANOTHER");
	}
	
}


测试代码如下所示。

package reflectTest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**测试正常调用和反射调用下的执行效率问题。
 * 测试范围:构造函数,get,set方法;
 * 测试类:TestBean;
 * 测试过程:分别执行1,00,000,000次,统计时间;
 * 测试环境:JDK1.8,Intel i3,双核四线程,2.13GHz,内存2GB
 * */
public class ReflectTest {
	
	public static void main(String[]args){
		//普通调用
		TestBeanRun normal=new TestBeanRun();
		TestBeanCount.testBean(normal);
		//
		TestBeanRun norRef = new TestBeanRun() {
			@Override
			public void testConstruct() {
				Class<?> clazz;
				try {
//					clazz = TestBean.class;
					clazz=Class.forName("reflectTest.TestBean");
					super.tb = (TestBean) clazz.newInstance();
				} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
					e.printStackTrace();
				}
			}

			@Override
			public void testGet() {
				try {
					Method m = TestBean.class.getMethod("getWord");
					s = (String) m.invoke(tb);
				} catch (NoSuchMethodException | SecurityException
						| IllegalAccessException | IllegalArgumentException
						| InvocationTargetException e) {
				}

			}

			@Override
			public void testSet() {
				try {
					Method m = TestBean.class.getMethod("setWord",String.class);
					m.invoke(tb, "testSet");
				} catch (NoSuchMethodException | SecurityException
						| IllegalAccessException | IllegalArgumentException
						| InvocationTargetException e) {
				}
			}
		};
		TestBeanCount.testBean(norRef);
		TestBeanCount.testBean(new TestBeanRun(){
			Class<?>clazz=TestBean.class;
			Method mget;
			Method mset;
			{
				try {
					mget=TestBean.class.getMethod("getWord");
					mset=TestBean.class.getMethod("setWord",String.class);
				} catch (NoSuchMethodException | SecurityException  | IllegalArgumentException e) {
				}
			}
			@Override
			public void testConstruct() {
				try {
					super.tb=(TestBean) clazz.newInstance();
				} catch (InstantiationException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
			@Override
			public void testGet() {
				try {
					s=(String)mget.invoke(tb,null);
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				}
						
			}
			@Override
			public void testSet() {
				try {
					mset.invoke(tb,"testSet");
				} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				}
			}
		});
	}
	
	
}


这里对每个单元测试了十遍,结果如下所示。

普通调用普通反射缓存的反射
methodtime/msavr/nstime/msavr/nstime/msavr/ns
construct9709.70 1454011454.01 229422.94
get6376.37 30690306.90 153615.36
set6826.82 32830328.30 241324.13
construct8838.83 1439281439.28 212221.22
get6116.11 44981449.81 147214.72
set7077.07 55214552.14 238223.82
construct8938.93 2510542510.54 212521.25
get6196.19 52653526.53 144314.43
set7137.13 58730587.30 237023.70
construct8908.90 1889711889.71 213121.31
get5935.93 29949299.49 142914.29
set6566.56 32276322.76 240024.00
construct9019.01 1418051418.05 210121.01
get5775.77 30169301.69 145214.52
set6546.54 31594315.94 238723.87
construct8958.95 1389481389.48 214421.44
get5705.70 29739297.39 147114.71
set6496.49 31638316.38 240324.03
construct8648.64 1376251376.25 211321.13
get5965.96 30532305.32 142714.27
set6766.76 31447314.47 235223.52
construct9049.04 1384631384.63 214321.43
get6006.00 29677296.77 142914.29
set6746.74 31492314.92 238323.83
construct8898.89 1395241395.24 210521.05
get5975.97 29642296.42 142614.26
set6746.74 31579315.79 242924.29
construct8658.65 1382461382.46 212821.28
get5735.73 29879298.79 142314.23
set6526.52 31571315.71 238623.86
有结果可知。在JDK1.8下,常规调用和反射调用的差距并没有那么大。普通的反射调用的速度很差,原因在于调用了Class.forName查找类很费时,getMethod查找方法也很费时。如果可以将类或者方法预先缓存下来再执行,也就是第三种方法,则效率差不太多了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: