Java反射:为什么这么慢?Java反射:为什么这么慢?(Java Reflection: Why

2019-05-13 00:28发布

我一直回避基于其缓慢的声誉Java反射负全部。 我达到了我的当前项目的设计,其中能够使用它会让我的代码更具可读性和优雅的一个点,所以我决定搏一搏。

我只是通过差异感到惊讶,我注意到,有时几乎是100倍更长的运行时间。 即使在这个简单的例子,它只是实例化一个空类,这是令人难以置信。

class B {

}

public class Test {

    public static long timeDiff(long old) {
        return System.currentTimeMillis() - old;
    }

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

        long numTrials = (long) Math.pow(10, 7);

        long millis;

        millis = System.currentTimeMillis();

        for (int i=0; i<numTrials; i++) {
            new B();
        }
        System.out.println("Normal instaniation took: "
                 + timeDiff(millis) + "ms");

        millis = System.currentTimeMillis();

        Class<B> c = B.class;

        for (int i=0; i<numTrials; i++) {
            c.newInstance();
        }

        System.out.println("Reflecting instantiation took:" 
              + timeDiff(millis) + "ms");

    }
}

因此,其实,我的问题是

  • 为什么是这样慢? 是不是我做错了吗? (以上连示例演示的差)。 我有一个很难相信它真的可以比正常的要慢实例100倍。

  • 有没有别的东西,可以更好地用于治疗代码的数据(请记住我坚持使用Java现在)

Answer 1:

您的测试可能存在缺陷。 一般来说尽管JVM可以优化正常的实例,但不能作出反射使用情况的优化 。

对于那些想知道什么时间是,我添加了一个预热阶段和使用的阵列保持创建的对象(更类似于一个真正的程序可能会做)。 我跑我的OSX,JDK7的系统上测试代码,并得到这个:

反映实例拿了:5180ms
普通实例化的了:2001ms

修改的测试:

public class Test {

    static class B {

    }

    public static long timeDiff(long old) {
        return System.nanoTime() - old;
    }

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

        int numTrials = 10000000;
        B[] bees = new B[numTrials];
        Class<B> c = B.class;
        for (int i = 0; i < numTrials; i++) {
            bees[i] = c.newInstance();
        }
        for (int i = 0; i < numTrials; i++) {
            bees[i] = new B();
        }

        long nanos;

        nanos = System.nanoTime();
        for (int i = 0; i < numTrials; i++) {
            bees[i] = c.newInstance();
        }
        System.out.println("Reflecting instantiation took:" + TimeUnit.NANOSECONDS.toMillis(timeDiff(nanos)) + "ms");

        nanos = System.nanoTime();
        for (int i = 0; i < numTrials; i++) {
            bees[i] = new B();
        }
        System.out.println("Normal instaniation took: " + TimeUnit.NANOSECONDS.toMillis(timeDiff(nanos)) + "ms");
    }


}


Answer 2:

反思是几个原因很明显慢:

  1. 编译器可以做到没有任何的优化,因为它可以对你正在做什么,没有真正的想法。 这大概无二JIT以及
  2. 一切都被调用/创造了被发现 (即类抬头的名字,方法看着火柴等)
  3. 参数需要通过装箱/拆箱到打扮,包装成数组, Exceptions裹着InvocationTargetException S和重新抛出等等。
  4. 所有的处理乔恩斯基特提到这里 。

仅仅因为一些是100X慢并不意味着它是给你的假设反思是“正道”为您设计您的程序太慢 。 例如,我想象的IDE大量使用反射和我的IDE主要是从性能的角度确定。

毕竟, 反射的开销很可能是小巫见大巫 ,比如说相比 ,解析XML或访问数据库

另外要记住的一点是, 微基准测试是确定的东西快是如何在实践中出了名的有缺陷的机制 。 除了蒂姆·本德的言论时,JVM需要时间“热身”时,JIT可以重新优化代码热点上即时等。



Answer 3:

为实例B中的实时编译的代码是令人难以置信的轻巧。 基本上,它需要分配足够的内存(这是刚刚递增,除非需要GC指针),这就是它 - 没有构造函数的代码来真的叫; 我不知道是否JIT跳过它或没有,但无论哪种方式,有没有很多工作要做。

相比之下,与反射必须做的一切:

  • 检查是否有一个参数的构造函数
  • 检查参数的构造函数的可访问性
  • 检查调用者可以访问使用反射在所有
  • 工作了(在执行时)需要多少空间来分配
  • 打电话到构造函数代码(因为它不会预先知道,构造函数为空)

...大概其他的事情我还没有想到的。

通常反射不是在性能关键上下文中使用; 如果你需要这样的动态行为,你可以使用类似BCEL代替。



Answer 4:

看来,如果你做的构造函数访问,它会执行得更快。 现在,它比其他版本慢了约10-20倍。

    Constructor<B> c = B.class.getDeclaredConstructor();
    c.setAccessible(true);
    for (int i = 0; i < numTrials; i++) {
        c.newInstance();
    }

Normal instaniation took: 47ms
Reflecting instantiation took:718ms

如果你使用服务器VM,它能够更加优化,所以,这只是3-4倍慢。 这是很典型的表现。 文章认为地理链接是一个很好看的。

Normal instaniation took: 47ms
Reflecting instantiation took:140ms

但是,如果你能标替换-XX:+ DoEscapeAnalysis那么JVM能够优化常规实例远(这将是0-15ms),而反射实例保持不变。



Answer 5:

  • 反思是当首次推出很慢,但已经在新的JRE大大加快
  • 不过,这可能不是一个内部循环使用反射个好主意
  • 基于反射的代码具有基于JIT的优化低电位
  • dependeny注入框架,实例化JDBC实现类或XML解析器:反射在查找具体的类和方法,其中仅接口是已知的连接松耦合组件,即,大多使用。 这些用途通常可以在系统启动时进行一次,所以小的低效率不怎样都无所谓!


Answer 6:

@Tim本德尔的代码给我的机器上,这些结果(jdk_1.8_45,OS_X 10.10,i7处理器,16G):

Reflecting instantiation took:1139ms Normal instaniation took: 4969ms

如此看来现代JVM,反射代码也将被优化的不错。



文章来源: Java Reflection: Why is it so slow?