Performance Comparison Between JDK Reflection and Spring's ReflectionUtils
This article analyzes the performance differences between native Java reflection and Spring Framework's ReflectionUtils by running benchmark tests, examining source code caching mechanisms, and explaining why ReflectionUtils can be faster for repeated method and field access in backend applications.
Preface – A developer named Xiao Cai needed to use reflection for a generic requirement and was warned by the technical lead that native reflection can be slow, suggesting the use of Spring's ReflectionUtils utility.
Performance Test of Native Reflection – An entity class is created, and native reflection is used to instantiate objects, invoke methods, and modify fields. The benchmark results show that invoking reflection 10,000 times takes about 12 ms, 1,000,000 times takes 285 ms, and 10,000,000 times takes 3.198 s.
Native Reflection Benchmark
Invocation Count
1
1_000
10_000
1_000_000
10_000_000
Time (ms)
2
4
12
285
3198
Although the numbers look acceptable for occasional use, large‑scale reflection can still impose noticeable overhead, especially on production servers without high‑end hardware.
Spring ReflectionUtils Benchmark – Using ReflectionUtils to perform the same operations yields the following results, showing a higher cost on the first call but better performance on subsequent calls.
ReflectionUtils Benchmark
Invocation Count
1
1_000
10_000
1_000_000
10_000_000
Native Time (ms)
2
4
12
285
3198
ReflectionUtils Time (ms)
49
4
8
74
495
The comparison reveals that ReflectionUtils incurs a heavy initialization cost but becomes faster than native reflection when the method count reaches millions, achieving more than a six‑fold speedup.
Source Code Analysis – The key to the performance gain lies in caching. ReflectionUtils maintains strong‑reference caches such as private static final Map , Method[]> declaredMethodsCache and private static final Map , Field[]> declaredFieldsCache , avoiding repeated native lookups. Native reflection uses ReflectionData with soft references, which can be cleared by the GC, forcing re‑introspection.
Native Class.getDeclaredMethod and Class.getDeclaredField first check the security manager, then retrieve method/field arrays via privateGetDeclaredMethods or privateGetDeclaredFields . These methods consult ReflectionData ; if the cache is missing, they invoke native VM calls and store the results in a soft‑reference cache.
Spring's ReflectionUtils.findMethod and ReflectionUtils.findField directly access the cached arrays (or populate them once) and perform simple linear scans, eliminating the extra factory copy step that native reflection performs ( getReflectionFactory().copyMethod(...) ).
Conclusion – For backend projects that heavily rely on reflection, using Spring's ReflectionUtils can reduce the overhead of repeated method/field lookups because its caches are strong references and it avoids the costly factory copy operations. However, the initial cache warm‑up is expensive, so the utility shines only after the first few thousand invocations.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.