Geoffrey De Smet Geoffrey De Smet - 6 months ago 24
Java Question

Calling a getter in Java though reflection: What's the fastest way to repeatedly call it (performance and scalability wise)?

Given a class

Foo
and a property
bar
, neither of which I know at compile time, I need to repeatedly call the getter
Foo.getBar()
many, many times.

Suppose I have:

PropertyDescriptor barDescriptor = ...; // Don't worry how I got this


And suppose I need to do something like this:

Method barReadMethod = barDescriptor.getReadMethod();
for (Object foo : fooList) { // 1000000000 elements in fooList
Object bar = barReadMethod.invoke(foo);
...
}


The implementation above is still very slow compared to just calling it without reflection. Is there a faster way?

What's the fastest way of calling a getter with reflection in Java?

Answer

Calling barReadMethod.setAccessible(true); turns off the security checks which can make it a bit faster. Even if it is accessible, it has to check otherwise.

If I run use a getter method with and without accessible true.

class Main {
    static class A {
        private final Integer i;

        A(Integer i) {
            this.i = i;
        }

        public Integer getI() {
            return i;
        }
    }

    public static void main(String... args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        A[] as = new A[100000];
        for (int i = 0; i < as.length; i++)
            as[i] = new A(i);

        for (int i = 0; i < 5; i++) {
            long time1 = timeSetAccessible(as);
            long time2 = timeNotSetAccessible(as);
            System.out.printf("With setAccessible true %.1f ns, Without setAccessible %.1f ns%n",
                   (double) time1 / as.length, (double) time2 / as.length);
        }
    }

    static long dontOptimiseAvay = 0;

    private static long timeSetAccessible(A[] as) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method getter = A.class.getDeclaredMethod("getI");
        getter.setAccessible(true);
        dontOptimiseAvay = 0;
        long start = System.nanoTime();
        for (A a : as) {
            dontOptimiseAvay += (Integer) getter.invoke(a);
        }
        return System.nanoTime() - start;
    }

    private static long timeNotSetAccessible(A[] as) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Method getter = A.class.getDeclaredMethod("getI");
//        getter.setAccessible(true);
        dontOptimiseAvay = 0;
        long start = System.nanoTime();
        for (A a : as) {
            dontOptimiseAvay += (Integer) getter.invoke(a);
        }
        return System.nanoTime() - start;
    }
}

prints

With setAccessible true 106.4 ns, Without setAccessible 126.9 ns
With setAccessible true 5.4 ns, Without setAccessible 29.4 ns
With setAccessible true 3.2 ns, Without setAccessible 9.9 ns
With setAccessible true 3.1 ns, Without setAccessible 9.0 ns
With setAccessible true 3.1 ns, Without setAccessible 8.9 ns

For a simple getter, using setAccessible(true) can be three times faster.