Java Reflection:为什么这么慢?

2022-08-31 17:45:32

我一直避免使用Java反射soley,因为它以缓慢而闻名。在我当前项目的设计中,我达到了一个点,能够使用它将使我的代码更具可读性和优雅性,所以我决定试一试。

我对这种差异感到惊讶,我注意到有时运行时间几乎延长了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)


答案 1

反射缓慢有几个明显的原因:

  1. 编译器不能进行任何优化,因为它可能不知道你在做什么。这可能也适用于JIT
  2. 必须发现正在调用/创建的所有内容(即按名称查找的类,查看匹配项的方法等)
  3. 参数需要通过装箱/拆箱,打包成数组,包裹在s中并重新抛出等方式进行装扮。ExceptionsInvocationTargetException
  4. Jon Skeet在这里提到的所有处理。

仅仅因为某些东西慢了100倍,并不意味着它对你来说太慢了,假设反射是你设计程序的“正确方法”。例如,我认为IDE大量使用反射,从性能角度来看,我的IDE基本上没问题。

毕竟,与解析XML或访问数据库相比反射的开销可能显得微不足道

要记住的另一点是,微基准是一种众所周知的有缺陷的机制,用于确定实践中的速度。除了Tim Bender的评论之外,JVM需要时间来“预热”,JIT可以动态地重新优化代码热点等。


答案 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");
    }


}

推荐