你是否听到人们说“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
暂无评论