Java Reflection Performance — Why It's Slow and What MethodHandle / LambdaMetafactory Do Differently
Fast recall
Quick revision
Core answer
invoke, blocking method-level optimizations. `MethodHandle` (Java 7+) is faster — usually 2–5x slower than direct after warm-up — because it exposes call-site details to the JIT via invokedynamic. `LambdaMetafactory` is the fastest option: it generates a tiny class that calls the target directly, producing near-zero overhead. Practical guidance: reflection is fine at framework init time (once per startup). For hot paths, cache MethodHandles or use LambdaMetafactory to produce a Function/Supplier you can call like a direct reference.Whether the candidate understands the three distinct reasons reflection is slow, the faster alternatives, and can articulate when each is appropriate
Claiming reflection is 'slow' without numbers or reasons. Not knowing `MethodHandle` exists. Forgetting that `setAccessible(true)` + caching mitigates much of the cost
Benchmark numbers from JMH (direct ~1ns, reflective ~50ns, MethodHandle ~5ns, LambdaMetafactory ~1-2ns post-warmup). Mention that Spring 5+ uses `LambdaMetafactory` for `@Autowired` setter injection for speed
Interview-ready guide
Interview answer
Reflection is slow for three stackable reasons — and knowing each one is how you pick the right mitigation.
The three costs of `Method.invoke`
- Access check on every call unless you cached the Method with
setAccessible(true)already applied - Argument boxing — primitives become
Object[];intarguments allocateIntegerper call - JIT can't inline through
Method.invoke— the target method stays opaque; no loop unrolling, no escape analysis
Rough JMH numbers
- Direct call: ~1 ns
- Reflection with cached Method + setAccessible: ~30–80 ns (~30x slower)
MethodHandleafter JIT warm-up: ~5–10 nsLambdaMetafactory-generatedFunction.apply: ~1–2 ns, essentially native
`MethodHandle` (Java 7+)
- Uses
invokedynamicso the JIT can see and optimize the call site - No per-call access check; primitive handles avoid boxing
- Trade-off:
invokeExactrequires exact type match — more rigid, but that's what enables the optimization
`LambdaMetafactory`
- Generates a tiny adapter class at startup that calls the target directly
- Result is a regular
FunctionorSupplier— you call it like any normal method reference - Spring 5+ uses this for
@Autowiredsetter injection
Mitigations without changing APIs
- Cache the
Method/Field/Constructor— lookup is 10–100x slower than invocation setAccessible(true)once, then invoke many times- Avoid reflection inside tight loops — move it to init time
Modern alternatives
- Annotation processors (Dagger, Micronaut, MapStruct) generate accessor code at compile time — zero runtime cost
- GraalVM native-image compiles ahead-of-time with limited reflection — design to minimize reflection or use configuration hints
Recommendation: Reflection at framework startup is fine — the cost amortizes across the application lifetime. For hot paths, profile first: if reflection shows up in top-10 CPU samples, cache MethodHandles or use LambdaMetafactory to produce a direct-call-speed Function. And for new framework code that needs high throughput, consider annotation processors — they trade build time for zero runtime cost, which is the right call for most performance-sensitive use cases.
Java Reflection Performance — Why It's Slow and What `MethodHandle` / `LambdaMetafactory` Do Differently
Direct Answer
Method.invoke is ~10–50x slower than a direct call for three reasons: (1) every call goes through a security/accessibility check unless cached; (2) primitive arguments are boxed into Object[]; (3) the JIT can rarely inline through invoke, blocking method-level optimizations. MethodHandle (Java 7+) is faster — usually 2–5x slower than direct after warm-up — because it exposes call-site details to the JIT via invokedynamic. LambdaMetafactory is the fastest option: it generates a tiny class that calls the target directly, producing near-zero overhead. Practical guidance: reflection is fine at framework init time (once per startup). For hot paths, cache MethodHandles or use LambdaMetafactory to produce a Function/Supplier you can call like a direct reference.
Common Mistakes & Pitfalls
Claiming reflection is 'slow' without numbers or reasons. Not knowing MethodHandle exists. Forgetting that setAccessible(true) + caching mitigates much of the cost
Check your understanding
Answer these without looking back at the article.
- Why does `setAccessible(true)` improve reflection performance?
- How does `invokedynamic` let `MethodHandle` be faster than `Method.invoke`?
- Why does Spring 5 use `LambdaMetafactory` instead of direct reflection for setter injection?
- What does `GeneratedMethodAccessor` do, and why does the JVM switch to it after many invocations?
- When would annotation processors be a better choice than runtime reflection?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.