Fundamentals 9 min read

Inside Java’s Method.invoke: How Reflection Works Under the Hood

This article dissects Java's reflection core by walking through the Method.invoke implementation, the MethodAccessor hierarchy, the inflation mechanism, and the proxy pattern that switches from native to generated accessors after fifteen calls, revealing why reflection can be both fast and slow.

Senior Brother's Insights
Senior Brother's Insights
Senior Brother's Insights
Inside Java’s Method.invoke: How Reflection Works Under the Hood

Reflection‑related Classes

Beyond the basic classes covered earlier (Field, Method, Constructor), Java’s reflection API includes less‑known types such as AccessibleObject, ReflectionFactory, and MethodAccessor, which together enable the powerful but complex reflective operations.

Focus on Method.invoke

The article concentrates on the Method.invoke method, the centerpiece of Java reflection, and explains the typical stack traces (e.g., sun.reflect.NativeMethodAccessorImpl.invoke0) that appear when frameworks invoke methods via reflection.

Source‑code Walkthrough

The first layer of Method.invoke looks like this:

@CallerSensitive
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor; // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

The @CallerSensitive annotation prevents privilege escalation by skipping the annotated method when determining the real caller class.

After the preliminary checks, the method obtains a MethodAccessor instance. If it is null, acquireMethodAccessor() creates one.

MethodAccessor Interface and Implementations

MethodAccessor

defines a single invoke method:

public interface MethodAccessor {
    Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException;
}

There are three concrete implementations:

sun.reflect.DelegatingMethodAccessorImpl
sun.reflect.MethodAccessorImpl
sun.reflect.NativeMethodAccessorImpl

Creating a MethodAccessor

acquireMethodAccessor()

either reuses an existing accessor from the root or creates a new one via ReflectionFactory.newMethodAccessor(this):

private MethodAccessor acquireMethodAccessor() {
    MethodAccessor tmp = null;
    if (root != null) tmp = root.getMethodAccessor();
    if (tmp != null) {
        methodAccessor = tmp;
    } else {
        tmp = reflectionFactory.newMethodAccessor(this);
        setMethodAccessor(tmp);
    }
    return tmp;
}

The factory method decides between a fast native accessor and a generated Java accessor based on the "inflation" flag:

public MethodAccessor newMethodAccessor(Method method) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
        return (new MethodAccessorGenerator()).generateMethod(...);
    } else {
        NativeMethodAccessorImpl nativeAcc = new NativeMethodAccessorImpl(method);
        DelegatingMethodAccessorImpl deleg = new DelegatingMethodAccessorImpl(nativeAcc);
        nativeAcc.setParent(deleg);
        return deleg;
    }
}

This uses the proxy pattern: DelegatingMethodAccessorImpl forwards calls to the underlying NativeMethodAccessorImpl.

DelegatingMethodAccessorImpl Details

class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
    private MethodAccessorImpl delegate;
    DelegatingMethodAccessorImpl(MethodAccessorImpl d) { setDelegate(d); }
    public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
        return delegate.invoke(obj, args);
    }
    void setDelegate(MethodAccessorImpl d) { this.delegate = d; }
}

The invoke method also contains the inflation logic:

public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
    if (++this.numInvocations > ReflectionFactory.inflationThreshold() &&
        !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
        MethodAccessorImpl gen = (new MethodAccessorGenerator()).generateMethod(...);
        this.parent.setDelegate(gen);
    }
    return invoke0(this.method, obj, args);
}

The static field inflationThreshold defaults to 15. When the number of invocations exceeds this threshold, the JVM replaces the native accessor with a generated Java accessor, which runs faster after the initial warm‑up.

Why Inflation Matters

Native reflection is quick for the first few calls but degrades as invocation count grows. The generated Java accessor is slower to load but becomes faster after inflation, reducing overall startup cost for applications that heavily use reflection.

Sequence Diagram

In summary, Java’s reflection mechanism starts with a native accessor for speed, switches to a generated accessor after fifteen invocations, and employs the proxy pattern to keep the implementation interchangeable while preserving security checks via @CallerSensitive.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

inflationmethod.invokemethodaccessorproxy-pattern
Senior Brother's Insights
Written by

Senior Brother's Insights

A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.