反思工作
在Java中,人们普遍认为反射-使用java.reflect
API会在性能方面付出高昂的代价。 较旧的Java版本具有巨大的性能开销,而较新的版本似乎使它处于可接受的范围内。 但是“可以接受”的真正含义是什么?
这是我在评论性能评估时提出的问题,该评估建议根据标准代码的反映来替换代码。 由于我们的许多决定不是基于事实而是基于信念,因此我决定执行一些测试以获取Java 8中的指标。
测试协议
为了通过不受挑战的协议获得现实的指标,我使用了出色的JMH测试框架。 JMH具有许多优势,其中包括:
- 现有的Maven工件很容易获得
- 基准测试方法仅需使用
@Benchmark
注释 - 它处理JVM的热身
- 它还可以在控制台上处理结果写入
这是一个JMH片段:
@Benchmark
publicvoidexecutePerformanceTest(){
// Code goes here
}
JMH将负责执行上述executePerformanceTest()
并负责计算所花费的时间。
代码
为了强调反射的成本,让我们检查使用反射访问属性和不使用反射调用简单吸气剂所需时间之间的差异。
// With reflection
FieldfirstName=clazz.getDeclaredField("firstName");
FieldlastName=clazz.getDeclaredField("lastName");
FieldbirthDate=clazz.getDeclaredField("birthDate");
Field.setAccessible(newAccessibleObject[]{firstName,lastName,birthDate},true);
firstName.get(person);
lastName.get(person);
birthDate.get(person);
// Without reflection
person.getFirstName();
person.getLastName();
person.getBirthDate();
检查可能的优化
我想知道是否将不可变数据结构编译为优化的字节码 ,从而减少反射的性能开销。
因此,我以两种不同的方式创建了相同的基本数据结构:
- 一个带有无参数构造函数和setter的可变项
- 一个具有
final
属性和构造函数初始化的不变性
结果
在我的机器上运行测试会产生以下结果:
# Run complete. Total time: 00:26:55 Benchmark Mode Cnt Score Error Units BenchmarkRun.runImmutableWithReflection thrpt 200 2492673.501 ± 37994.941 ops/s BenchmarkRun.runImmutableWithoutReflection thrpt 200 26499946.587 ± 242499.198 ops/s BenchmarkRun.runMutableWithReflection thrpt 200 2505239.277 ± 27697.028 ops/s BenchmarkRun.runMutableWithoutReflection thrpt 200 26635097.050 ± 150798.911 ops/s
对于精打细算的读者:
不反射 | 反射 | |
---|---|---|
Mutable |
26,635,097.050 ± 150,798.911 |
2,505,239.277 ± 27,697.028 |
Immutable |
26,499,946.587 ± 242,499.198 |
2,492,673.501 ± 37,994.941 |
对于感兴趣的条形图的读者(请注意比例是线性的 ):
结论
无论以何种方式显示结果,在我的机器*问具有反射的字段所需的时间要比不使用反射的字段多十倍。 我的猜测是,这可以推断为在任何机器上都是正确的。
有一个必然的结论:无论您认为在给定的时间是否成立,都应始终进行一些事实检查以确保您的决策具有坚实的基础。
翻译自: https://blog.frankel.ch/performance-cost-of-reflection/
反思工作