Java反射

Java反射機制在程序運行時,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。這種動態的獲取信息以及動態調用對象的方法 的功能稱爲java的反射機制。java

首先你須要瞭解類加載的過程,這裏咱們簡單提一下(加載-驗證-準備-解析-初始化),反射是靠JVM和Class相關類實現的。api

按照這個例子,咱們調試下看看具體實現。併發

@Data
public class Person {
    private String name;

    public static void main(String[] args) throws Exception {
        Person person = new Person();
        person.setName("lewis");

        for (int i = 0; i < 16; i++) {
            Method method = Person.class.getMethod("getName");
            System.out.println(method.invoke(person));
        }
    }
}

 

@CallerSensitive
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
       InvocationTargetException
{
    if (!override) {
        // 檢查方法是否爲public
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            // 權限校驗
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    // MethodAccessor實現有兩個版本,一個是Java實現的,另外一個是JNI實現的
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

咱們上面提到了 MethodAccessor 有兩個實現,Java版本和JNI版本(就是java native),ide

Java實現的版本在初始化時須要較多時間,但長久來講性能較好;JNI版本正好相反,啓動時相對較快,但運行時間長了以後速度就比不過Java版了。
 
爲了儘量地減小性能損耗,HotSpot JDK採用「inflation」的技巧:讓Java方法在被反射調用時,開頭若干次使用JNI版,等反射調用次數超過閾值(15)時則生成一個專用的MethodAccessor實現類,生成其中的invoke()方法的字節碼,之後對該Java方法的反射調用就會使用Java版本。 
 
ReflectionFactory.newMethodAccessor()生產MethodAccessor對象的邏輯,一開始(JNI版)會生產NativeMethodAccessorImpl和DelegatingMethodAccessorImpl兩個對象。
    public MethodAccessor newMethodAccessor(Method var1) {
        checkInitted();
        if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;
        }
    }
DelegatingMethodAccessorImpl的源碼以下:
這是一箇中間層,方便在JNI版本與Java版的MethodAccessor之間實現切換。
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
    private MethodAccessorImpl delegate;

    DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {
        this.setDelegate(var1);
    }

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        return this.delegate.invoke(var1, var2);
    }

    void setDelegate(MethodAccessorImpl var1) {
        this.delegate = var1;
    }
}

來看看NativeMethodAccessorImpl實現,超過15次之後調用反射,就會經過咱們上面提到的中間層 DelegatingMethodAccessorImpl 所引用的 MethodAccessor 都是java 版。高併發

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private final Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;

    NativeMethodAccessorImpl(Method var1) {
        this.method = var1;
    }

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        // 方法被調用時,程序調用計數器都會增長1,看看是否超過閾值
        if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            // 超過15次 則調用MethodAccessorGenerator.generateMethod()來生成Java版的MethodAccessor的實現類
            // 而且改變經過中間層,後續DelegatingMethodAccessorImpl所引用的MethodAccessor改成Java版
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }
        // native版本,JNI方式調用
        return invoke0(this.method, var1, var2);
    }

    void setParent(DelegatingMethodAccessorImpl var1) {
        this.parent = var1;
    }

    private static native Object invoke0(Method var0, Object var1, Object[] var2);
}

在默認狀況下,方法的反射調用爲委派實現,委派給本地實現來進行方法調用。在調用超過 15 次以後,委派實現便會將委派對象切換至動態實現。這個動態的字節碼是在Java運行過程當中經過ASM自動生成的,它將直接使用 invoke 指令來調用目標方法。工具

繼續查看代碼,能夠看到sun.reflect.MethodAccessorGenerator#generate的實現是調用asm字節碼加強工具來生成類,此過程較長,不在此列出。在該方法的最後,咱們發現有這樣一個操做sun.reflect.ClassDefiner#defineClass,查看其源碼post

static Class<?> defineClass(String name, byte[] bytes, int off, int len,
                                final ClassLoader parentClassLoader)
    {
        // 建立一個DelegatingClassLoader用來加載生成的類
        ClassLoader newLoader = AccessController.doPrivileged(
            new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                        return new DelegatingClassLoader(parentClassLoader);
                    }
                });
        return unsafe.defineClass(name, bytes, off, len, newLoader, null);
}

 

參考:

一個關於log4j2的高併發問題性能

相關文章
相關標籤/搜索