我知道,使用反射API,我们可以使用存储在字符串中的方法的名称来调用它们。
但是,反射API不能用于高性能的应用程序。在我的应用程序中,将以非常高的速度调用方法。因此,我不能使用反射API。
那么,反射API的替代方案是什么呢?
我做了研究,发现cglib和其他代码生成库可以使用。
但是,我没有找到通过存储在字符串中的方法名称来调用方法的任何示例。
使用反射替代方案也是一个很好的例子。
更新:,实际上我正在实现一些主从通信API.其中,奴隶将远程调用主方法。而且,方法调用将以非常高的速率(每秒Approx 50方法调用)。作为,主人不断地轮询奴隶的任何响应。那么,我应该以如此高的调用率进行反思吗?
发布于 2014-05-10 10:51:51
Cglib附带了一个名为FastMethod的类。这个类的目的是使用非反射接口来调用给定的方法。为此,FastMethod实现了接口,并生成字节代码来调用指定的方法,从而避免了据称代价高昂的反射调用。
但是,这里有两个原因说明为什么您很可能不使用这个类。克莱布是很久以前写的。在这些日子里,反射比今天还要昂贵。然而,现代JVM 通货膨胀率。默认情况下,JVM将在第15次反射调用之后生成用于调用方法的字节代码。这正是cglib提供给您的明确要求。
此外,最昂贵的不是反射调用,而是查找。您仍然需要将方法命名为FastMethod。因此,即使使用cglib,也无法避免这些成本。
因此,我建议您依赖反射,直到您真正确定这是一个性能瓶颈。至少,使用像JMH这样的工具来证明这种实现是合理的。另外,考虑到类会消耗perm / meta空间,这会导致给用户带来的麻烦。
发布于 2014-05-10 10:54:19
人们普遍错误地认为,反思是缓慢的。那是在Java1.3左右的时候。
但是在现代Java中,反射确实得到了很好的优化。它使用引擎盖下的动态字节码生成。此外,JVM可以直接将这些调用内联到调用方中,因此具有反射的方法的调用几乎与直接调用一样快。
这里的其他评论建议使用cglib的FastMethod。事实上,它并不比反射更快。下面是用著名的JMH框架编写的基准测试:
@State(Scope.Benchmark)
public class MethodInvoke {
Method method;
FastMethod fastMethod;
@Setup
public void init() throws NoSuchMethodException {
method = getClass().getMethod("foo", int.class, long.class);
method.setAccessible(true);
fastMethod = FastClass.create(getClass()).getMethod("foo", new Class[] { int.class, long.class });
}
@GenerateMicroBenchmark
public long fastMethod() throws Exception {
return (Long) fastMethod.invoke(this, new Object[] {2, 3L});
}
@GenerateMicroBenchmark
public long reflection() throws Exception {
return (Long) method.invoke(this, 2, 3L);
}
public long foo(int a, long b) {
return a + b;
}
}Java 7u51 (64位)的结果:
Benchmark Mode Samples Mean Mean error Units
b.MethodInvoke.fastMethod thrpt 5 79248,583 3978,941 ops/ms
b.MethodInvoke.reflection thrpt 5 76975,414 2844,730 ops/ms您知道,FastMethod.invoke仅比Method.invoke快3%,但是与反射相比,FastMethod没有执行正确的参数验证等等。
发布于 2014-05-10 11:24:11
通常,对方法的内省调用需要两个阶段:首先需要定位要调用的目标方法,然后在目标实例上调用该方法,为其提供参数。
在这个过程中,最昂贵的操作是在类中定位目标方法(例如,一旦您掌握了方法对象,就调用对象上的方法,比如:Method method = TargetClass.getMethod(Class[] signature ...)是一个轻量级操作,比直接方法调用稍微贵一些。
为了演示这一点,我对这三种方法进行了快速而肮脏的比较,结果如下(您需要比较它们之间的关系):
请注意,内省有固定的性能成本,所以方法所做的计算越多,这些数字就越接近。
在以前的项目中,性能是非常重要的,我使用了这种方法-缓存方法。在实践中,您可以看到,实际的方法执行时间使得内省的成本可以被否定。(cfr .Amdahal定律 )
测试代码(承受快速和肮脏)
import java.lang.reflect.Method;
import java.util.Random;
/**
* Created by maasg on 5/10/14.
*/
public class Instrospection {
public static void main(String [] params) throws Exception {
Random ran = new Random();
String[] methods = new String[] {"method1", "method2", "method3"};
Target target = new Target();
// Warmup
for (int i=0; i<1000; i++) {
String methodName = methods[ran.nextInt(3)];
String param = new Integer(ran.nextInt()).toString();
Method method = Target.class.getMethod(methodName, String.class);
}
StringBuilder builder = new StringBuilder();
long t0 = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
String methodName = methods[ran.nextInt(3)];
String param = new Integer(ran.nextInt()).toString();
Method method = Target.class.getMethod(methodName, String.class);
Object result = method.invoke(target, "param");
builder.append(result.toString());
}
System.out.println("Elapsed 1: "+(System.currentTimeMillis()-t0));
Method[] invokeMethods = new Method[] {
Target.class.getMethod(methods[0], String.class),
Target.class.getMethod(methods[1], String.class),
Target.class.getMethod(methods[2], String.class),
};
builder = new StringBuilder();
long t1 = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
String param = new Integer(ran.nextInt()).toString();
Method method = invokeMethods[ran.nextInt(3)];
Object result = method.invoke(target, "param");
builder.append(result.toString());
}
System.out.println("Elapsed 2: "+(System.currentTimeMillis()-t1));
builder = new StringBuilder();
long t2 = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
Object result = null;
String param = new Integer(ran.nextInt()).toString();
switch (ran.nextInt(3)) {
case 0: result = target.method1(param);
case 1: result = target.method2(param);
case 2: result = target.method3(param);
}
builder.append(result.toString());
}
System.out.println("Elapsed 3: "+(System.currentTimeMillis()-t2));
}
}https://stackoverflow.com/questions/23579737
复制相似问题