4年前 (2020-11-08)  Java系列 |   抢沙发  547 
文章评分 0 次,平均分 0.0

你是否听到人们说“Java反射太慢了”或“使用反射刚刚降低了性能”,但我还没有听到任何确切的数字。不如我们做些测试来了解真实情况。

下面我编写了一些示例代码来对典型的Java方法调用与基于反射的方法调用进行比较。结果正是我对JVM的期望。任何可优化的函数(比如返回一个常量,或者一个只依赖于输入的方法,或者像交换内存中的整数这样的简单操作)都会因为在方法中执行的工作太快而出现明显的减速。但是,当该方法必须执行任何类型的I/O或复杂任务(如字符串连接或随机数生成)时,差异变得非常小。

因此,下面的结果表明,除了最琐碎的操作之外,反射只是一个很小的开销,有时甚至不存在。有时不存在,因为方法本身内部性能的可变性可能会消除反射的成本。反射并不缓慢。

import java.lang.reflect.*;

public class IsReflectionSlow {
  public static double rand() {
    return Math.random();
  }

  public static String constant() {
    return "constant";
  }

  public static String concat(String[] strings) {
    StringBuilder sb = new StringBuilder();
    for (String s : strings)
      sb.append(s).append(" ");
    return sb.toString();
  }

  public static void main(String[] args) throws Exception {
    testConstant();
    testRand();
    testConcat(args);
  }

  private static void testRand() throws Exception {
    System.out.println("***** Testing Random number generation *****");
    long startStatic = System.currentTimeMillis();
    for (int i = 0; i < 100_000_000; i++)
      rand();
    long stopStatic = System.currentTimeMillis();
    System.out.println("Static test results: " + (stopStatic – startStatic));

    Method m = IsReflectionSlow.class.getMethod("rand", new Class[0]);
    Object[] params = new Object[0];
    long startReflect = System.currentTimeMillis();
    for (int i = 0; i < 100_000_000; i++)
      m.invoke(null, params);
    long stopReflect = System.currentTimeMillis();
    System.out.println("Reflection test results: " + (stopReflect – startReflect));
  }

  private static void testConstant() throws Exception {
    System.out.println("***** Testing Constants *****");
    long startStatic = System.currentTimeMillis();
    for (int i = 0; i < 100_000_000; i++)
      constant();
    long stopStatic = System.currentTimeMillis();
    System.out.println("Static test results: " + (stopStatic – startStatic));

    Method m = IsReflectionSlow.class.getMethod("constant", new Class[0]);
    Object[] params = new Object[0];
    long startReflect = System.currentTimeMillis();
    for (int i = 0; i < 100_000_000; i++)
      m.invoke(null, params);
    long stopReflect = System.currentTimeMillis();
    System.out.println("Reflection test results: " + (stopReflect – startReflect));
  }

  private static void testConcat(String[] list) throws Exception {
    System.out.println("***** Testing Concats *****");
    long startStatic = System.currentTimeMillis();
    for (int i = 0; i < 10_000_000; i++)
      concat(list);
    long stopStatic = System.currentTimeMillis();
    System.out.println("Static test results: " + (stopStatic – startStatic));

    Method m = IsReflectionSlow.class.getMethod("concat", new Class[] {String[].class});
    long startReflect = System.currentTimeMillis();
    for (int i = 0; i < 10_000_000; i++)
      m.invoke(null, (Object) list);
    long stopReflect = System.currentTimeMillis();
    System.out.println("Reflection test results: " + (stopReflect – startReflect));
  }
}

输出结果:

java IsReflectionSlow a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a
***** Testing Constants *****
Static test results: 2
Reflection test results: 212
***** Testing Random number generation *****
Static test results: 2234
Reflection test results: 2697
***** Testing Concats *****
Static test results: 16341
Reflection test results: 16870

因为反射涉及动态解析的类型,所以某些Java虚拟机优化无法执行。因此,反射操作的性能比非反射的操作慢,在性能敏感的应用程序中经常调用的代码部分应该避免。

在使用反射时,您所采取的每一步都需要经过验证。例如,当您调用一个方法时,它需要检查目标是否实际上是该方法的声明器的实例、是否有正确数量的参数、每个参数的类型是否正确等等。

绝对没有内联或其他性能技巧的可能性。

如果您按名称查找类型或方法,那么最多只需要一个简单的映射查找—每次执行时都会执行,而不是在JIT时执行一次。

基本上还有很多事情要做。然而,反射比过去快得多。。。如果你发现它太慢了,你很可能过度使用它。此时你可以考虑使用一些替代方案代替反射的功能:https://javakk.com/772.html

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/781.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册