Java 反射优化

Java About 5,424 words

示例代码

public class ReflectOptimizeDemo {

    public static void main(String[] args) throws Exception {
        Method getInfo = TestObject.class.getMethod("getInfo");
        TestObject testObject = TestObject.class.getDeclaredConstructor().newInstance();

        // 前 16 次调用,性能较低
        // 第 17 次开始,性能变高
        for (int i = 0; i < 17; i++) {
            System.out.printf("%d\t", i);
            getInfo.invoke(testObject);
        }
        System.in.read();
    }

}

public class TestObject {

    public String name;

    public int age;

    public String getInfo() {
        System.out.println("getInfo..." + new Exception().getStackTrace()[1].getClassName());
        return this.name + "-" + this.age;
    }

}

输出:可以看到第17次,方法调用的对象是GeneratedMethodAccessor1

0    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
1    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
2    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
3    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
4    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
5    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
6    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
7    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
8    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
9    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
10    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
11    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
12    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
13    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
14    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
15    getInfo...jdk.internal.reflect.NativeMethodAccessorImpl
16    getInfo...jdk.internal.reflect.GeneratedMethodAccessor1

原理

JDK中,当反射代码调用超过16次时(即:第17时开始,在内存中生成实例对象,加速调用)。

// java.lang.reflect.Method#invoke
public final class Method extends Executable {
    @CallerSensitive
    @ForceInline // to ensure Reflection.getCallerClass optimization
    @HotSpotIntrinsicCandidate
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz,
                        Modifier.isStatic(modifiers) ? null : obj.getClass(),
                        modifiers);
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }
}

numInvocations大于了ReflectionFactory.inflationThreshold()阈值(可以忽略后面的isVMAnonymousClass判断条件),开始生成访问类。

具体生成的方法在jdk.internal.reflect.MethodAccessorGenerator#generate中。

// jdk.internal.reflect.NativeMethodAccessorImpl#invoke
class NativeMethodAccessorImpl extends MethodAccessorImpl {
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        // We can't inflate methods belonging to vm-anonymous classes because
        // that kind of class can't be referred to by name, hence can't be
        // found from the generated bytecode.
        if (++numInvocations > ReflectionFactory.inflationThreshold()
                && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
            MethodAccessorImpl acc = (MethodAccessorImpl)
                new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),
                                   method.getName(),
                                   method.getParameterTypes(),
                                   method.getReturnType(),
                                   method.getExceptionTypes(),
                                   method.getModifiers());
            parent.setDelegate(acc);
        }

        return invoke0(method, obj, args);
    }
}

主要方法

Method类中的invoke()方法。

NativeMethodAccessorImpl类中的invoke()方法。

ReflectionFactory类中的inflationThreshold()checkInitted()方法。

优化开关

设置直接生成实例对象,如果设置为false,则直接生成实例对象。

-Dsun.reflect.noInflation=true

设置膨胀阈值,默认15(循环从0-15,共16次)

-Dsun.reflect.inflationThreshold=10

源码

// jdk.internal.reflect.ReflectionFactory#checkInitted
public class ReflectionFactory {

    private static void checkInitted() {
        if (initted) return;

        // Defer initialization until module system is initialized so as
        // to avoid inflation and spinning bytecode in unnamed modules
        // during early startup.
        if (!VM.isModuleSystemInited()) {
            return;
        }

        Properties props = GetPropertyAction.privilegedGetProperties();
        String val = props.getProperty("sun.reflect.noInflation");
        if (val != null && val.equals("true")) {
            noInflation = true;
        }

        val = props.getProperty("sun.reflect.inflationThreshold");
        if (val != null) {
            try {
                inflationThreshold = Integer.parseInt(val);
            } catch (NumberFormatException e) {
                throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
            }
        }

        disableSerialConstructorChecks =
            "true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));

        initted = true;
    }
}
Views: 1,019 · Posted: 2023-06-02

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh