java反射到底有多慢

java反射慢吗

java反射慢,我想这个现象大家都知道,但是为什么慢,到底慢多少,这个好像还真没有人深究过,今天就来探索一下

先写benchmark

这是git仓库的地址
java-reflect-benchmark

影响反射快慢的方式有哪些

  1. 调用方式,使用Method还是MethodHandle
  2. 是否使用多态,即是使用接口调用,还是使用类调用
  3. 形参的数量,因为动态需要做检查,所以数量多了,就表现出来了
  4. 形参是对象还是primitive类型,因为涉及到装箱,拆箱
  5. jdk版本的差异,比如8 11 17 21等等

测试结果

我在自己机器上跑的,只是简要测试,并不太严谨,严谨的测试,应该专门找一个没有任何负载的物理机器,专门跑这个测试,才能排除一切的干扰,
不过最后的结果也很有参考价值,先上jdk8的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Benchmark                                                Mode  Cnt  Score   Error  Units
ReflectTest.genericDirectCall avgt 3 0.239 ± 0.023 ns/op
ReflectTest.genericMethodHandleCall avgt 3 1.815 ± 0.236 ns/op
ReflectTest.genericReflectionCall avgt 3 2.139 ± 0.754 ns/op
ReflectTest.noParameterDirectCall avgt 3 0.237 ± 0.015 ns/op
ReflectTest.noParameterMethodHandleCall avgt 3 1.823 ± 0.198 ns/op
ReflectTest.noParameterTestReflectionCall avgt 3 1.672 ± 0.452 ns/op
ReflectTest.oneObjectParameterDirectCall avgt 3 0.241 ± 0.022 ns/op
ReflectTest.oneObjectParameterMethodHandleCall avgt 3 1.818 ± 0.304 ns/op
ReflectTest.oneObjectParameterReflectionCall avgt 3 2.120 ± 1.026 ns/op
ReflectTest.oneParameterInterfaceMethodDirectCall avgt 3 0.329 ± 0.052 ns/op
ReflectTest.oneParameterInterfaceMethodMethodHandleCall avgt 3 1.839 ± 0.328 ns/op
ReflectTest.oneParameterInterfaceMethodReflectionCall avgt 3 2.140 ± 0.625 ns/op
ReflectTest.onePrimateParameterDirectCall avgt 3 0.246 ± 0.061 ns/op
ReflectTest.onePrimateParameterMethodHandleCall avgt 3 1.786 ± 0.190 ns/op
ReflectTest.onePrimateParameterReflectionCall avgt 3 2.275 ± 0.519 ns/op
ReflectTest.override1DirectCall1 avgt 3 0.241 ± 0.028 ns/op
ReflectTest.override1DirectCall2 avgt 3 0.249 ± 0.043 ns/op
ReflectTest.override1DirectCall3 avgt 3 0.250 ± 0.020 ns/op
ReflectTest.override1MethodHandleCall1 avgt 3 1.848 ± 0.648 ns/op
ReflectTest.override1MethodHandleCall2 avgt 3 1.795 ± 0.229 ns/op
ReflectTest.override1MethodHandleCall3 avgt 3 1.810 ± 0.086 ns/op
ReflectTest.override1ReflectionCall1 avgt 3 2.049 ± 0.461 ns/op
ReflectTest.override1ReflectionCall2 avgt 3 2.087 ± 0.063 ns/op
ReflectTest.override1ReflectionCall3 avgt 3 2.549 ± 0.369 ns/op
ReflectTest.threeObjectParameterDirectCall avgt 3 0.245 ± 0.023 ns/op
ReflectTest.threeObjectParameterMethodHandleCall avgt 3 1.822 ± 0.097 ns/op
ReflectTest.threeObjectParameterReflectionCall avgt 3 2.553 ± 0.175 ns/op
ReflectTest.threePrimateParameterDirectCall avgt 3 0.246 ± 0.120 ns/op
ReflectTest.threePrimateParameterMethodHandleCall avgt 3 1.820 ± 0.363 ns/op
ReflectTest.threePrimateParameterReflectionCall avgt 3 4.038 ± 0.640 ns/op
ReflectTest.twoObjectParameterDirectCall avgt 3 0.253 ± 0.019 ns/op
ReflectTest.twoObjectParameterMethodHandleCall avgt 3 1.797 ± 0.232 ns/op
ReflectTest.twoObjectParameterReflectionCall avgt 3 2.175 ± 0.405 ns/op
ReflectTest.twoPrimateParameterDirectCall avgt 3 0.249 ± 0.031 ns/op
ReflectTest.twoPrimateParameterMethodHandleCall avgt 3 1.799 ± 0.207 ns/op
ReflectTest.twoPrimateParameterReflectionCall avgt 3 2.980 ± 0.725 ns/op

这是jdk21的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Benchmark                                                Mode  Cnt  Score   Error  Units
ReflectTest.genericDirectCall avgt 3 0.259 ± 0.111 ns/op
ReflectTest.genericMethodHandleCall avgt 3 2.092 ± 0.183 ns/op
ReflectTest.genericReflectionCall avgt 3 3.778 ± 0.286 ns/op
ReflectTest.noParameterDirectCall avgt 3 0.264 ± 0.095 ns/op
ReflectTest.noParameterMethodHandleCall avgt 3 1.936 ± 0.251 ns/op
ReflectTest.noParameterTestReflectionCall avgt 3 3.522 ± 0.401 ns/op
ReflectTest.oneObjectParameterDirectCall avgt 3 0.258 ± 0.134 ns/op
ReflectTest.oneObjectParameterMethodHandleCall avgt 3 1.997 ± 0.286 ns/op
ReflectTest.oneObjectParameterReflectionCall avgt 3 3.738 ± 0.169 ns/op
ReflectTest.oneParameterInterfaceMethodDirectCall avgt 3 0.400 ± 0.029 ns/op
ReflectTest.oneParameterInterfaceMethodMethodHandleCall avgt 3 2.037 ± 0.350 ns/op
ReflectTest.oneParameterInterfaceMethodReflectionCall avgt 3 4.038 ± 0.027 ns/op
ReflectTest.onePrimateParameterDirectCall avgt 3 0.256 ± 0.020 ns/op
ReflectTest.onePrimateParameterMethodHandleCall avgt 3 1.955 ± 0.053 ns/op
ReflectTest.onePrimateParameterReflectionCall avgt 3 4.077 ± 0.326 ns/op
ReflectTest.override1DirectCall1 avgt 3 0.265 ± 0.139 ns/op
ReflectTest.override1DirectCall2 avgt 3 0.256 ± 0.028 ns/op
ReflectTest.override1DirectCall3 avgt 3 0.256 ± 0.023 ns/op
ReflectTest.override1MethodHandleCall1 avgt 3 1.993 ± 0.303 ns/op
ReflectTest.override1MethodHandleCall2 avgt 3 2.086 ± 0.172 ns/op
ReflectTest.override1MethodHandleCall3 avgt 3 2.085 ± 0.174 ns/op
ReflectTest.override1ReflectionCall1 avgt 3 3.706 ± 0.149 ns/op
ReflectTest.override1ReflectionCall2 avgt 3 3.830 ± 0.201 ns/op
ReflectTest.override1ReflectionCall3 avgt 3 4.042 ± 0.418 ns/op
ReflectTest.threeObjectParameterDirectCall avgt 3 0.272 ± 0.025 ns/op
ReflectTest.threeObjectParameterMethodHandleCall avgt 3 2.095 ± 0.212 ns/op
ReflectTest.threeObjectParameterReflectionCall avgt 3 4.038 ± 0.442 ns/op
ReflectTest.threePrimateParameterDirectCall avgt 3 0.262 ± 0.067 ns/op
ReflectTest.threePrimateParameterMethodHandleCall avgt 3 2.076 ± 0.121 ns/op
ReflectTest.threePrimateParameterReflectionCall avgt 3 4.546 ± 2.780 ns/op
ReflectTest.twoObjectParameterDirectCall avgt 3 0.257 ± 0.016 ns/op
ReflectTest.twoObjectParameterMethodHandleCall avgt 3 2.104 ± 0.344 ns/op
ReflectTest.twoObjectParameterReflectionCall avgt 3 3.860 ± 0.397 ns/op
ReflectTest.twoPrimateParameterDirectCall avgt 3 0.262 ± 0.081 ns/op
ReflectTest.twoPrimateParameterMethodHandleCall avgt 3 1.990 ± 0.337 ns/op
ReflectTest.twoPrimateParameterReflectionCall avgt 3 4.267 ± 0.496 ns/op

具体的结果,可以在git仓库的doc目录找到,而且这个项目开箱即用,如果想测试的话,自己本机测试跑跑也可以的

结论

  1. 反射从微观层面来说,增加了至少7.69倍的调用时间(最简单的无形参调用),如果是原始类型的多形参调用,以3个原始类型为例,竟然可以是16.14倍,从微观层面讲,的确反射很慢
  2. 反射,装箱,拆箱的话,效率更低,的确原始类型的拆箱装箱大量调用,对性能有一定的损耗
  3. MethodHandle相比Method的确存在优化,在jdk21的时候,一些反射耗时较长的时候,竟然能达到它的一半
  4. 反射从微观层面讲,一个方法的调用增加了7.69~16.14倍的调用时间,但是实际情况下,方法如果本身的执行时间比较长,这个时间其实是可以忽略的,因为本身方法的调用时间就不长,
    但是如果是大量耗时十分短的方法,长时间大量调用,这个就比较可观了
  5. 现在编程的环境,开发效率大于执行效率,反射其实是个非常强大的特性,经常被框架大量使用
  6. spring就重度使用反射,几乎spring的所有功能都依赖反射,但是spring的效率低下,反射只是其中一个原因

后话

总体来说,java反射的慢是可以接受的,但是,现在去反射也有这样的框架,比如micronaut,就通过annotationProcessor生成静态的类,然后避免了反射,启动速度大幅降低,
如果能消除反射,程序的性能也是可以得到一定程度的提高的