使用反射调用方法的性能

时间:2014-08-19 16:53:46

标签: java reflection

使用反射调用方法与直接调用方法有什么性能差异?

上下文详细信息: 我有一个类,有一些字符串属性,对于某些属性,我必须将值设置为小写或大写(以及与之相关的一些逻辑)。

我有两个选择:

  1. 复制/粘贴14个属性的代码。
  2. 或注释这14种方法并使用反射找到它们并调用它们(也使用反射)。
  3. 当然使用选项2.我的代码更易读,更易于维护,但问题在于性能。如果性能影响不超过20秒,我会有几百万(大约2-3个)实例,我会选择反射选项。

    这里有没有人有这方面的经验?

3 个答案:

答案 0 :(得分:3)

很难给出一个固定的数字,因为它取决于你系统的许多方面,所以我写了一个类来检查它。

结果让我感到惊讶,似乎对于我的系统而言,简单地通过反射调用方法与静态调用方法相比,静态调用在非空方法的10,000之间和空方法的1000,000倍之间。

我使用了两者,以防java编译器/ vm优化掉一个空方法调用。

代码如下,也可在此要点中找到 - https://gist.github.com/piersy/23b4814eec806b584266

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

public class TestReflection {

    public static long INC = 0;

    public static final int COUNT = 100000000;

    private static class Dummy {
        public void doSomething(String s1, String s2, String s3) {
            INC++;
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {

        Dummy obj = new Dummy();
        Method method = obj.getClass().getMethod("doSomething", String.class, String.class, String.class);

        String s1 = "string1";
        String s2 = "string2";
        String s3 = "string3";

        //warmup
        runReflection(obj, method, s1, s2, s3, COUNT / 10);
        runStatic(obj, s1, s2, s3, COUNT/10);

        ///realtest

        long reftime = System.nanoTime();
        runReflection(obj, method, s1, s2, s3, COUNT);
        reftime = System.nanoTime() - reftime;

        long time = System.nanoTime();
        runStatic(obj, s1, s2, s3, COUNT);
        time = System.nanoTime() - time;

        System.out.println(reftime);
        System.out.println(time);

        //1000 *1000 *1000 nanoseconds in a second
        System.out.println(reftime / (1000 *1000 *1000));
        System.out.println(time / (1000 *1000 *1000));
        System.out.println((double)reftime/ (double)time );

        System.out.println("percentage speed decrease from using reflection:"+(((double)reftime/(double)time)-1)*100);

    }

    private static void runReflection(Dummy obj, Method method, String s1, String s2, String s3, int count) {
        for (int i = 0; i < count; i++) {
            try {
                method.invoke(obj, s1, s2, s3);
            } catch (IllegalArgumentException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
            }
        }
    }

    private static void runStatic(Dummy obj, String s1, String s2, String s3, int count) {
        for (int i = 0; i < count; i++) {
            obj.doSomething(s1,s2,s3);
        }
    }

}

答案 1 :(得分:1)

一般来说,是的,反射速度较慢。如果数以百万计的实例意味着数百万的呼叫,那么是的,在某些情况下,您可能会产生超过20秒的开销。在某些情况下,您可能不会产生额外的开销,因为编译器将能够优化它。您要么必须提供更多有关计划如何使用反射或自行运行的详细信息。我的GUT说,在300万次通话中,你不会产生超过20秒的开销,但这是非常直接的感觉。

答案 2 :(得分:0)

让我们从Oracle Docs

的反思开始
  

反思的缺点

     

反思很强大,但不应随意使用。如果它   可以在不使用反射的情况下执行操作,然后执行操作   最好避免使用它。应保留以下问题   记住通过反射访问代码时。

     

性能开销因为反射涉及的类型   动态解析,某些Java虚拟机优化可以   不被执行。因此,反射操作较慢   性能比他们的非反光同行,应该是   在经常调用的代码段中避免使用   对性能敏感的应用程序。

     

安全限制   Reflection需要运行时权限,运行时可能不存在   在安全经理下。这是一个重要的考虑因素   必须在受限制的安全上下文中运行的代码,例如在   小应用程序。

     

内部曝光   由于反射允许代码执行在非反射代码中非法的操作,例如   访问私有字段和方法,可以导致使用反射   在意想不到的副作用,这可能导致代码功能失调和   可能会破坏便携性。反射代码打破了抽象和   因此可能会通过升级平台来改变行为。

因此,一旦开始扩展,您可以确保性能成为问题。 但是,如果您能够忍受直觉所说的大写/小写操作不应超过20秒的性能开销,并且可以大幅度扩展,那么您可以使用它。但请注意,对性能的直觉通常总是错误的。所以你想要测试一下。

为所需任务查找面向方面编程。

我的观点:使用AOP框架。