3

怎样提高反射的效率?

weknow 336
2015-07-20 提问
7 个回答
6

一. 善用API

比如,尽量不要getMethods()后再遍历筛选,而直接用getMethod(methodName)来根据方法名获取方法

二、缓存大法好~

比如,需要多次动态创建一个类的实例的时候,有缓存的写法会比没有缓存要快很多:

// 1. 没有缓存
void createInstance(String className){
    return Class.forName(className).newInstance();
}

// 2. 缓存forName的结果
void createInstance(String className){
    cachedClass = cache.get(className);
    if (cachedClass == null){
        cachedClass = Class.forName(className);
        cache.set(className, cachedClass);
    }
    return cachedClass.newInstance();
}

为什么?当然是因为forName太耗时了。

p.s. cache请自行实现

2
  1. 在系统启动阶段使用反射。
  2. 将反射得到元数据保存起来,使用时,只需从内存中调用即可。
  3. hotspot虚拟机会对执行次数较多的方法进行优化(例如使用jit技术)。
  4. 使用高性能的反射库,应该会比自己写缓存效果好。
javapublic Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

private Method getMethod0(String name, Class<?>[] parameterTypes) {
        if ((res = searchMethods(privateGetDeclaredMethods(true),
                                 name,
                                 parameterTypes)) != null) {
            return res;
        }
        if (!isInterface()) {
            Class<? super T> c = getSuperclass();
            if (c != null) {
                if ((res = c.getMethod0(name, parameterTypes)) != null) {
                    return res;
                }
            }
        }
        Class<?>[] interfaces = getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            Class<?> c = interfaces[i];
            if ((res = c.getMethod0(name, parameterTypes)) != null) {
                return res;
            }
        }
        return null;
    }

private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }    

顺便看一下getMethod方法,如果类的方法不是很多,getMethod和getMethods方法差距不是很大。差距在于自己写轮寻和系统自带轮寻之间的效率。

2

说了半天,都没有人提到"setAccessible(true)"...楼上的各位不是资深java程序员吧?

edit:
实话地告诉你,JDK1.6之后,对于method/field/constructor的invoke这类的反射,除了"setAccessible(true)"之外,再无须其它任何优化, 完爆以前cglib的fastmethod之流

当然了,将得到的method/field/constructor对象做缓存这是基本的

07-31 edit:
忘了补充一句:
如果你在jdk6上跑,且如果你反射的目标方法是getter/setter methods的话,记得加上配置:-XX:-UseFastEmptyMethods -XX:-UseFastAccessorMethods , 这两个配置的关闭是为了让accessor methods能够被jit; jdk7以上不需要设置这两个配置

0

除了缓存还有没其他方法?

0

要使用类信息,少查找class, field, method,缓存很重要,还有办法是生成代理类。要复制对象可以用CGLib的BeanCopier (其原理是运行时动态生成了用于复制某个类的代码(字节码))。
要提高Method.invoke性能,可用Java 7的MethodHandle (由于Method.invoke的JIT优化,差距不大)。
使用高版本JDK也很重要,反射性能一直在提高。

0

ReflectASM 通过字节码生成的方式加快反射速度

0

使用反射框架,如joor,或者apache的commons相关工具类

撰写答案

推广链接