首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >调用方法的名称存储在没有反射API的字符串中?

调用方法的名称存储在没有反射API的字符串中?
EN

Stack Overflow用户
提问于 2014-05-10 10:10:06
回答 4查看 6.1K关注 0票数 4

我知道,使用反射API,我们可以使用存储在字符串中的方法的名称来调用它们。

但是,反射API不能用于高性能的应用程序。在我的应用程序中,将以非常高的速度调用方法。因此,我不能使用反射API。

那么,反射API的替代方案是什么呢?

我做了研究,发现cglib和其他代码生成库可以使用。

但是,我没有找到通过存储在字符串中的方法名称来调用方法的任何示例。

使用反射替代方案也是一个很好的例子。

更新:,实际上我正在实现一些主从通信API.其中,奴隶将远程调用主方法。而且,方法调用将以非常高的速率(每秒Approx 50方法调用)。作为,主人不断地轮询奴隶的任何响应。那么,我应该以如此高的调用率进行反思吗?

EN

回答 4

Stack Overflow用户

发布于 2014-05-10 10:51:51

Cglib附带了一个名为FastMethod的类。这个类的目的是使用非反射接口来调用给定的方法。为此,FastMethod实现了接口,并生成字节代码来调用指定的方法,从而避免了据称代价高昂的反射调用。

但是,这里有两个原因说明为什么您很可能不使用这个类。克莱布是很久以前写的。在这些日子里,反射比今天还要昂贵。然而,现代JVM 通货膨胀率。默认情况下,JVM将在第15次反射调用之后生成用于调用方法的字节代码。这正是cglib提供给您的明确要求。

此外,最昂贵的不是反射调用,而是查找。您仍然需要将方法命名为FastMethod。因此,即使使用cglib,也无法避免这些成本。

因此,我建议您依赖反射,直到您真正确定这是一个性能瓶颈。至少,使用像JMH这样的工具来证明这种实现是合理的。另外,考虑到类会消耗perm / meta空间,这会导致给用户带来的麻烦

票数 2
EN

Stack Overflow用户

发布于 2014-05-10 10:54:19

人们普遍错误地认为,反思是缓慢的。那是在Java1.3左右的时候。

但是在现代Java中,反射确实得到了很好的优化。它使用引擎盖下的动态字节码生成。此外,JVM可以直接将这些调用内联到调用方中,因此具有反射的方法的调用几乎与直接调用一样快。

这里的其他评论建议使用cglib的FastMethod。事实上,它并不比反射更快。下面是用著名的JMH框架编写的基准测试:

代码语言:javascript
复制
@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位)的结果:

代码语言:javascript
复制
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没有执行正确的参数验证等等。

票数 0
EN

Stack Overflow用户

发布于 2014-05-10 11:24:11

通常,对方法的内省调用需要两个阶段:首先需要定位要调用的目标方法,然后在目标实例上调用该方法,为其提供参数。

在这个过程中,最昂贵的操作是在类中定位目标方法(例如,一旦您掌握了方法对象,就调用对象上的方法,比如:Method method = TargetClass.getMethod(Class[] signature ...)是一个轻量级操作,比直接方法调用稍微贵一些。

为了演示这一点,我对这三种方法进行了快速而肮脏的比较,结果如下(您需要比较它们之间的关系):

  • 每次反射方法+调用:167 method
  • 类+调用中的第一个缓存方法: 36 ms
  • 直接调用:17

请注意,内省有固定的性能成本,所以方法所做的计算越多,这些数字就越接近。

在以前的项目中,性能是非常重要的,我使用了这种方法-缓存方法。在实践中,您可以看到,实际的方法执行时间使得内省的成本可以被否定。(cfr .Amdahal定律 )

测试代码(承受快速和肮脏)

代码语言:javascript
复制
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));

    }

}
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23579737

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档