java应用反射的时候,性能往往是java程序员担心的地方,那么在大量运用反射的时候,性能的微弱提升,对这个系统而言都是如旱地逢甘霖。
下面用代码简单验证一下反射性能都损耗在哪里。
package littlehow.reflect.demo;
/**
* WorkBean 作为反射性能测试的bean
*
* @author littlehow
* @time 2016-06-13 14:27
*/
public class WorkBean {
/**
* 工作人名称
*/
private String userName;
/**
* 公司名称
*/
private String companyName;
/**
* 工作年限
*/
private int workAge;
/**
* 薪资待遇
*/
private double salary;
/**
* 无参构造
*/
public WorkBean() {
super();
}
/**
* 姓名
* @param userName
*/
public WorkBean(String userName) {
this.userName = userName;
}
/**
* 姓名和公司名称的构造
* @param userName
* @param companyName
*/
public WorkBean(String userName, String companyName) {
this(userName);
this.companyName = companyName;
}
/**
* 工作
*/
public void workAt(boolean flag) {
System.out.println(this.userName+"在"+(flag?"上午":"下午")+"上午工作的时候一直在打瞌睡!");
}
/**
* 工作
* @param flag
* @param message
*/
public void workAt(boolean flag, String message) {
System.out.println(this.userName+"在"+(flag?"上午":"下午")+message);
}
/**
* 工作
*/
public void workAt() {
System.out.println(this.userName+"工作了好久了");
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public int getWorkAge() {
return workAge;
}
public void setWorkAge(int workAge) {
this.workAge = workAge;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return this.userName + "在" + this.companyName + "工作了" + this.workAge + "年,现在的薪水是" + this.salary;
}
}
然后是测试反射的demo
package littlehow.reflect.demo;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* ReflectDemo 反射性能消耗在哪里
*
* @author littlehow
* @time 2016-06-13 14:41
*/
public class ReflectDemo {
/**
* 测试class
*/
static Class<?> clazz = WorkBean.class;
/**
* 公用构造方法缓存
*/
private static final HashMap<String, Constructor<?>> constructors = new HashMap<String, Constructor<?>>();
/**
* 公用method缓存
*/
private static final ConcurrentHashMap<String, MethodCache> methods = new ConcurrentHashMap<String, MethodCache>();
/**
* 不经过优化的构造方法性能测试
*/
@Test
public void constructorNormalTest() {
long start = System.currentTimeMillis();
try {
Object obj = null;
for(int i = 0; i < 1000000; i ++) {
Constructor<?> cons1 = clazz.getConstructor();
obj = cons1.newInstance();
Constructor<?> cons2 = clazz.getConstructor(String.class);
obj = cons2.newInstance("littlehow");
Constructor<?> cons3 = clazz.getConstructor(String.class, String.class);
obj = cons3.newInstance("littlehow", "future company");
}
System.out.println(obj);//littlehow在future company工作了0年,现在的薪水是0.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均840毫秒左右
}
/**
* 对单一的进行缓存,速度最快,但功用性不够好
* 相对于
* @see constructorNormalTest()方法,性能提升10倍
* 可见反射中对构造方法的获取非一个非常耗时的操作
*/
@Test
public void constrctorBufferOne() {
long start = System.currentTimeMillis();
try {
Constructor<?> cons1 = clazz.getConstructor();
Constructor<?> cons2 = clazz.getConstructor(String.class);
Constructor<?> cons3 = clazz.getConstructor(String.class, String.class);
Object obj = null;
for(int i = 0; i < 1000000; i ++) {
obj = cons1.newInstance();
obj = cons2.newInstance("littlehow");
obj = cons3.newInstance("littlehow", "future company");
}
System.out.println(obj);//littlehow在future company工作了0年,现在的薪水是0.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均80毫秒左右
}
/**
* key值的计算耗时也比较大,可以再进行优化
* @param clazz
* @param arguClass
* @return
* @throws NoSuchMethodException
*/
private static Constructor<?> getConstrucor(Class<?> clazz, Class<?> ...arguClass) throws NoSuchMethodException {
String key = clazz.getName();
for (Class<?> argu : arguClass) {
key = key + argu.getName();//这里会有编译优化
}
Constructor<?> constructor = constructors.get(key);
if (constructor == null) {
constructor = clazz.getConstructor(arguClass);
constructors.put(key, constructor);
}
return constructor;
}
/**
* 公用缓存,在性能和开发维护中这种处理,在性能要求不是超高的
* 系统中可以选用该方式进行构造方法的缓存,提升开发速度
* 效率相对第一个测试性能也提升了一倍多
*/
@Test
public void constructorBufferAll() {
long start = System.currentTimeMillis();
try {
Constructor<?> cons1 = clazz.getConstructor();
Constructor<?> cons2 = clazz.getConstructor(String.class);
Constructor<?> cons3 = clazz.getConstructor(String.class, String.class);
Object obj = null;
for(int i = 0; i < 1000000; i ++) {
obj = getConstrucor(clazz).newInstance();
obj = getConstrucor(clazz, String.class).newInstance("littlehow");
obj = getConstrucor(clazz, String.class, String.class).newInstance("littlehow", "future company");
}
System.out.println(obj);//littlehow在future company工作了0年,现在的薪水是0.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均330毫秒左右
}
/**
* 用class直接newInstance
* 直接用class的newInstance方式性能非常高,
* 但是局限于newInstance只能调用无参构造方法;
* 因为newInstance调用的是其内部的cacheConstructor
* 所以实例化的时候非常快
* 所以如果是无参构造方法的话,就不需要单独获取constructor,
* 因为性能相差无几
*/
@Test
public void clazzNewInstance() {
long start = System.currentTimeMillis();
try {
Object obj = null;
for(int i = 0; i < 1000000; i ++) {
obj = clazz.newInstance();
}
System.out.println(obj);//null在null工作了0年,现在的薪水是0.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均20毫秒左右
}
/**
* 方法反射示例,方法每次都去反射中寻找
*/
@Test
public void methodNormalTest() {
long start = System.currentTimeMillis();
try {
Object obj = getConstrucor(clazz, String.class, String.class).newInstance("littlehow", "old company");
for (int i = 0; i < 1000000; i ++) {
Method m = clazz.getDeclaredMethod("setWorkAge", int.class);
m.invoke(obj, i);
m = clazz.getDeclaredMethod("setSalary", double.class);
m.invoke(obj, 3500.0);
}
System.out.println(obj);//littlehow在old company工作了999999年,现在的薪水是3500.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均850毫秒左右
}
/**
* 单独缓存方法,大规模反射应用不易管理
* 相对非缓存,性能提高了14倍
*/
@Test
public void methodBufferOne() {
long start = System.currentTimeMillis();
try {
Method m1 = clazz.getDeclaredMethod("setWorkAge", int.class);
Method m2 = clazz.getDeclaredMethod("setSalary", double.class);
Object obj = getConstrucor(clazz, String.class, String.class).newInstance("littlehow", "old company");
for (int i = 0; i < 1000000; i ++) {
m1.invoke(obj, i);
m2.invoke(obj, 3500.0);
}
System.out.println(obj);//littlehow在old company工作了999999年,现在的薪水是3500.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均56毫秒左右
}
/**
* 获取方法,这里只是最初略的写了个获取的方法
* @param clazz
* @param argus
* @return
*/
private static Method getMethod(Class<?> clazz, String name, Class<?> ...argus) throws NoSuchMethodException {
MethodCache mc = methods.get(clazz.getName());
if (mc == null) {
mc = new MethodCache(clazz);
methods.put(clazz.getName(), mc);
}
return mc.getMethod(name, argus);
}
/**
* 进一步缓存,牺牲内存提升速度,速度提升明显
*/
static class MethodCache {
private Class<?> clazz;
/**
* 缓存
*/
private Map<String, Method> methods = new HashMap<String, Method>();
public MethodCache(Class<?> clazz) {
this.clazz = clazz;
Method[] methodArr = clazz.getDeclaredMethods();
List<String> remove = new ArrayList<String>();
//私有的method不缓存,static方法不缓存
for (Method m : methodArr) {
if (!m.isAccessible() && ((1 & m.getModifiers()) > 0) && ((8 & m.getModifiers()) == 0)) {
Method old = methods.put(m.getName(), m);
//如果old不为空,表示方法多了重载,需要处理特殊处理
if (old != null) {
put(old);
put(m);
remove.add(m.getName());
}
}
}
for (String s : remove) {
methods.remove(s);
}
}
/**
* 按照参数进行put
* @param method
*/
private void put(Method method) {
Type[] types = method.getParameterTypes();
String key = method.getName() + types.length;
for (Type type : types) {
String str = type.toString();
if (str.startsWith("class ")) {
str = str.substring(6);
}
key += str;
}
methods.put(key, method);
}
/**
* 获取方法
* @param name
* @param argus
* @return
* @throws NoSuchMethodException
*/
public Method getMethod(String name, Class<?> ...argus) throws NoSuchMethodException{
Method m = methods.get(name);
if (m == null) {
name = name + argus.length;
for(Class<?> argu : argus) {
name = name + argu.getName();
}
m = methods.get(name);
}
return m;
}
}
/**
* 因为在大规模反射应用中,
* @see methodBufferOne()方法并不容易扩展
* 特别是自己写的动态代理进行aop切入时,
* 对一些方法进行置换处理时,就需要有个管理的缓存
*/
@Test
public void methodBufferAll() {
long start = System.currentTimeMillis();
try {
Object obj = getConstrucor(clazz, String.class, String.class).newInstance("littlehow", "old company");
//测试是否能获取不同的重载方法
getMethod(clazz, "workAt").invoke(obj);//littlehow工作了好久了
getMethod(clazz, "workAt", boolean.class).invoke(obj, false);//littlehow在下午上午工作的时候一直在打瞌睡!
getMethod(clazz, "workAt", boolean.class, String.class).invoke(obj, true, "一直努力解决问题");//littlehow在上午一直努力解决问题
for (int i = 0; i < 1000000; i ++) {
getMethod(clazz, "setWorkAge", int.class).invoke(obj, i);
getMethod(clazz, "setSalary", double.class).invoke(obj, 3500.0);
}
System.out.println(obj);//littlehow在old company工作了999999年,现在的薪水是3500.0
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("use time : " + (end - start) + "ms");//平均130毫秒左右
}
}
经过测试,可以发现,反射主要性能损耗是在对方法或变量的获取时,我们可以从这些方面入手,力求做到性能的极致。
因为我这里只是简单的demo,只是验证反射在哪些地方比较耗时,所以在处理性能优化方面没做进一步的改进。
而且从测试上也可以看出,反射其实没有想想中那么损耗性能,所以运用反射,速度上还是可以接受的。