javadoc缩进 - javadoc详解



Java反思:为什么这么慢? (5)

@Tim Bender的代码在我的机器上给出了这些结果(jdk_1.8_45,os_x 10.10,i7,16G):

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

所以在现代JVM上,反射代码也会很好地优化。

基于其缓慢的声誉,我总是避免使用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 #2

您的测试可能存在缺陷。 通常,虽然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 #3

用于实例化B的JITted代码非常轻量级。 基本上它需要分配足够的内存(除非需要GC,它只是增加一个指针)而这就是它 - 没有构造函数代码可以真正调用; 我不知道JIT是否会跳过它,但不管怎么说都没有太多可做的事情。

将其与反射必须做的所有事情进行比较:

  • 检查是否存在无参数构造函数
  • 检查无参数构造函数的可访问性
  • 检查调用者是否有权使用反射
  • 计算出(在执行时)需要分配多少空间
  • 调用构造函数代码(因为它不会事先知道构造函数为空)

......还有其他我甚至都没想过的事情。

通常,反射不用于性能关键的上下文中; 如果你需要这样的动态行为,你可以使用像BCEL这样的东西。


Answer #4
  • 首次引入时反射速度非常慢,但在较新的JRE中反应速度相当快
  • 尽管如此,在内循环中使用反射可能不是一个好主意
  • 基于反射的代码具有基于JIT的优化的低潜力
  • 反射主要用于连接松散耦合的组件,即查找具体的类和方法,其中只有接口是已知的:依赖注入框架,实例化JDBC实现类或XML解析器。 这些用途通常可以在系统启动时完成一次,因此效率低下无论如何都无关紧要!




reflection