In depth analysis of java reflection (VII) – briefly describe the underlying implementation of reflection calls

premise

The java reflection API is in javase 1 7, but Oracle jdk11 was used when writing this article, because jdk11 also uploaded the source code under the sun package. You can directly view the corresponding source code and debug through the IDE.

This paper mainly introduces the underlying implementation of reflection call. Of course, it has no ability to analyze the implementation of JVM. Here, we only analyze the call point of the final native method. The bottom layer will rely on unsafe class. If you can, please see an article written by the author before "magical magic and double-edged sword unsafe".

Research on the underlying implementation of reflection call

The following conditions are mainly considered:

The underlying implementation of processing attribute operations

The attribute operation methods field #set (object obj, object value) and field #get (object obj) are delegated to the JDK internal. reflect. Fieldaccessor implementation:

public interface FieldAccessor {
    /** Matches specification in {@link java.lang.reflect.Field} */
    public Object get(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public boolean getBoolean(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public byte getByte(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public char getChar(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public short getShort(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public int getInt(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public long getLong(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public float getFloat(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public double getDouble(Object obj) throws IllegalArgumentException;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void set(Object obj,Object value)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setBoolean(Object obj,boolean z)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setByte(Object obj,byte b)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setChar(Object obj,char c)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setShort(Object obj,short s)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setInt(Object obj,int i)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setLong(Object obj,long l)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setFloat(Object obj,float f)
        throws IllegalArgumentException,illegalaccessexception;

    /** Matches specification in {@link java.lang.reflect.Field} */
    public void setDouble(Object obj,double d)
        throws IllegalArgumentException,illegalaccessexception;
}

There are many implementations of the fieldaccessor interface. The fieldaccessor interface instance is implemented through JDK internal. reflect. Reflectionfactory is constructed by:

    public FieldAccessor newFieldAccessor(Field field,boolean override) {
        checkInitted();

        Field root = langReflectAccess.getRoot(field);
        if (root != null) {
            // FieldAccessor will use the root unless the modifiers have
            // been overrridden
            if (root.getModifiers() == field.getModifiers() || !override) {
                field = root;
            }
        }
        return UnsafeFieldAccessorFactory.newFieldAccessor(field,override);
    }

Finally delegate to unsafefieldaccessorfactory#newfieldaccessor():

class UnsafeFieldAccessorFactory {
    static FieldAccessor newFieldAccessor(Field field,boolean override) {
        Class<?> type = field.getType();
        boolean isStatic = Modifier.isStatic(field.getModifiers());
        boolean isFinal = Modifier.isFinal(field.getModifiers());
        boolean isVolatile = Modifier.isVolatile(field.getModifiers());
        boolean isQualified = isFinal || isVolatile;
        boolean isReadOnly = isFinal && (isStatic || !override);
        if (isStatic) {
            // This code path does not guarantee that the field's
            // declaring class has been initialized,but it must be
            // before performing reflective operations.
            UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(field.getDeclaringClass());

            if (!isQualified) {
                if (type == Boolean.TYPE) {
                    return new UnsafeStaticBooleanFieldAccessorImpl(field);
                } else if (type == Byte.TYPE) {
                    return new UnsafeStaticByteFieldAccessorImpl(field);
                } else if (type == Short.TYPE) {
                    return new UnsafeStaticShortFieldAccessorImpl(field);
                } else if (type == Character.TYPE) {
                    return new UnsafeStaticCharacterFieldAccessorImpl(field);
                } else if (type == Integer.TYPE) {
                    return new UnsafeStaticIntegerFieldAccessorImpl(field);
                } else if (type == Long.TYPE) {
                    return new UnsafeStaticLongFieldAccessorImpl(field);
                } else if (type == Float.TYPE) {
                    return new UnsafeStaticFloatFieldAccessorImpl(field);
                } else if (type == Double.TYPE) {
                    return new UnsafeStaticDoubleFieldAccessorImpl(field);
                } else {
                    return new UnsafeStaticObjectFieldAccessorImpl(field);
                }
            } else {
                if (type == Boolean.TYPE) {
                    return new UnsafeQualifiedStaticBooleanFieldAccessorImpl(field,isReadOnly);
                } else if (type == Byte.TYPE) {
                    return new UnsafeQualifiedStaticByteFieldAccessorImpl(field,isReadOnly);
                } else if (type == Short.TYPE) {
                    return new UnsafeQualifiedStaticShortFieldAccessorImpl(field,isReadOnly);
                } else if (type == Character.TYPE) {
                    return new UnsafeQualifiedStaticCharacterFieldAccessorImpl(field,isReadOnly);
                } else if (type == Integer.TYPE) {
                    return new UnsafeQualifiedStaticIntegerFieldAccessorImpl(field,isReadOnly);
                } else if (type == Long.TYPE) {
                    return new UnsafeQualifiedStaticLongFieldAccessorImpl(field,isReadOnly);
                } else if (type == Float.TYPE) {
                    return new UnsafeQualifiedStaticFloatFieldAccessorImpl(field,isReadOnly);
                } else if (type == Double.TYPE) {
                    return new UnsafeQualifiedStaticDoubleFieldAccessorImpl(field,isReadOnly);
                } else {
                    return new UnsafeQualifiedStaticObjectFieldAccessorImpl(field,isReadOnly);
                }
            }
        } else {
            if (!isQualified) {
                if (type == Boolean.TYPE) {
                    return new UnsafeBooleanFieldAccessorImpl(field);
                } else if (type == Byte.TYPE) {
                    return new UnsafeByteFieldAccessorImpl(field);
                } else if (type == Short.TYPE) {
                    return new UnsafeShortFieldAccessorImpl(field);
                } else if (type == Character.TYPE) {
                    return new UnsafeCharacterFieldAccessorImpl(field);
                } else if (type == Integer.TYPE) {
                    return new UnsafeIntegerFieldAccessorImpl(field);
                } else if (type == Long.TYPE) {
                    return new UnsafeLongFieldAccessorImpl(field);
                } else if (type == Float.TYPE) {
                    return new UnsafeFloatFieldAccessorImpl(field);
                } else if (type == Double.TYPE) {
                    return new UnsafeDoubleFieldAccessorImpl(field);
                } else {
                    return new UnsafeObjectFieldAccessorImpl(field);
                }
            } else {
                if (type == Boolean.TYPE) {
                    return new UnsafeQualifiedBooleanFieldAccessorImpl(field,isReadOnly);
                } else if (type == Byte.TYPE) {
                    return new UnsafeQualifiedByteFieldAccessorImpl(field,isReadOnly);
                } else if (type == Short.TYPE) {
                    return new UnsafeQualifiedShortFieldAccessorImpl(field,isReadOnly);
                } else if (type == Character.TYPE) {
                    return new UnsafeQualifiedCharacterFieldAccessorImpl(field,isReadOnly);
                } else if (type == Integer.TYPE) {
                    return new UnsafeQualifiedIntegerFieldAccessorImpl(field,isReadOnly);
                } else if (type == Long.TYPE) {
                    return new UnsafeQualifiedLongFieldAccessorImpl(field,isReadOnly);
                } else if (type == Float.TYPE) {
                    return new UnsafeQualifiedFloatFieldAccessorImpl(field,isReadOnly);
                } else if (type == Double.TYPE) {
                    return new UnsafeQualifiedDoubleFieldAccessorImpl(field,isReadOnly);
                } else {
                    return new UnsafeQualifiedObjectFieldAccessorImpl(field,isReadOnly);
                }
            }
        }
    }
}

Here, pay attention to the judgment of attribute modifiers:

Make judgment through the above modifier to get the final fieldaccessor implementation. Here's an example for analysis. For example, an ordinary non static property without volatile and final keywords will eventually get an instance of unsafeobjectfieldaccessorimpl:

class UnsafeObjectFieldAccessorImpl extends UnsafeFieldAccessorImpl {
    UnsafeObjectFieldAccessorImpl(Field field) {
        super(field);
    }

    public Object get(Object obj) throws IllegalArgumentException {
        ensureObj(obj);
        return unsafe.getObject(obj,fieldOffset);
    }

    public void set(Object obj,illegalaccessexception{
        ensureObj(obj);
        if (isFinal) {
            throwFinalFieldillegalaccessexception(value);
        }
        if (value != null) {
            if (!field.getType().isAssignableFrom(value.getClass())) {
                throwSetIllegalArgumentException(value);
            }
        }
        unsafe.putObject(obj,fieldOffset,value);
    }  

    public boolean getBoolean(Object obj) throws IllegalArgumentException {
        throw newGetBooleanIllegalArgumentException();
    }

    public byte getByte(Object obj) throws IllegalArgumentException {
        throw newGetByteIllegalArgumentException();
    }     

    // 省略其他直接抛出异常的方法 
}    

It can be seen that in unsafeobjectfieldaccessorimpl, except for get (object obj) and set (object obj, object value) methods, other methods directly throw illegalargumentexception. The underlying layers of get (object obj) and set (object obj, object value) depend on JDK internal. misc. Unsafe putobject (obj, value) and GetObject (obj, fieldoffset) methods. The memory offset address of the attribute is calculated in the constructor of the parent class unsafefieldaccessorimpl of unsafeobjectfieldaccessorimpl:

abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl {
    static final Unsafe unsafe = Unsafe.getUnsafe();

    protected final Field   field;
    protected final long    fieldOffset;
    protected final boolean isFinal;

    UnsafeFieldAccessorImpl(Field field) {
        this.field = field;
        if (Modifier.isStatic(field.getModifiers()))
            fieldOffset = unsafe.staticFieldOffset(field);
        else
            fieldOffset = unsafe.objectFieldOffset(field);
        isFinal = Modifier.isFinal(field.getModifiers());
    }
    // 省略其他方法 
}    

A summary can be made here. The setxx and getxx methods of the attribute reflection operation field are finally delegated to the JDK internal. misc. Unsafe putxx and getxx methods, and the memory offset address of the attribute is through JDK internal. misc. Unsafe is calculated by staticfieldbase(), staticfieldoffset and objectfieldoffset.

The underlying implementation that handles constructor calls

Constructor #newinstance() method call depends on constructor accessor:

    public T newInstance(Object ... initargs)
        throws InstantiationException,illegalaccessexception,IllegalArgumentException,InvocationTargetException
    {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller,clazz,modifiers);
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

// ConstructorAccessor接口
public interface ConstructorAccessor {
    /** Matches specification in {@link java.lang.reflect.Constructor} */
    public Object newInstance(Object[] args)
        throws InstantiationException,InvocationTargetException;
}

The constructoraccessor instance is also obtained through the reflection factory class reflectionfactory, specifically reflectionfactory#newconstructoraccessor:

    public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
        checkInitted();
        Class<?> declaringClass = c.getDeclaringClass();
        // 抽象方法会进入此if分支
        if (Modifier.isAbstract(declaringClass.getModifiers())) {
            return new InstantiationExceptionConstructorAccessorImpl(null);
        }
        // 宿主类直接是Class类型,则无法实例化
        if (declaringClass == Class.class) {
            return new InstantiationExceptionConstructorAccessorImpl
                ("Can not instantiate java.lang.Class");
        }

        // use the root Constructor that will not cache caller class
        Constructor<?> root = langReflectAccess.getRoot(c);
        if (root != null) {
            c = root;
        }

        // 当前声明构造的宿主类是ConstructorAccessorImpl的子类
        if (Reflection.isSubclassOf(declaringClass,ConstructorAccessorImpl.class)) {
            return new BootstrapConstructorAccessorImpl(c);
        }
        // 
        if (noInflation && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
            return new MethodAccessorGenerator().
                generateConstructor(c.getDeclaringClass(),c.getParameterTypes(),c.getExceptionTypes(),c.getModifiers());
        } else {
            NativeConstructorAccessorImpl acc =
                new NativeConstructorAccessorImpl(c);
            DelegatingConstructorAccessorImpl res =
                new DelegatingConstructorAccessorImpl(acc);
            acc.setParent(res);
            return res;
        }
    }

It can be seen that the final instance of constructoraccessor is delegatingconstructoraccessorimpl, and delegatingconstructoraccessorimpl is only a delegate implementation. The bottom layer is to call nativeconstructoraccessorimpl:

class NativeConstructorAccessorImpl extends ConstructorAccessorImpl {
    private final Constructor<?> c;
    private DelegatingConstructorAccessorImpl parent;
    private int numInvocations;

    NativeConstructorAccessorImpl(Constructor<?> c) {
        this.c = c;
    }

    public Object newInstance(Object[] args)
        throws InstantiationException,InvocationTargetException
    {
        // We can't inflate a constructor belonging to a vm-anonymous class
        // 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(c.getDeclaringClass())) {
            ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
                new MethodAccessorGenerator().
                    generateConstructor(c.getDeclaringClass(),c.getModifiers());
            parent.setDelegate(acc);
        }

        return newInstance0(c,args);
    }

    void setParent(DelegatingConstructorAccessorImpl parent) {
        this.parent = parent;
    }
    // 这个就是最终构造实例化对象的native方法
    private static native Object newInstance0(Constructor<?> c,Object[] args)
        throws InstantiationException,InvocationTargetException;
}

Nativeconstructoraccessorimpl#newinstance0 () is the native method that finally constructs the instantiated object. Of course, there are exceptions. For example, under abnormal calls, if the constructor's host class is an abstract class, an instantialexeptionconstructoraccessorimpl instance will be returned, in which an instantiationexception exception will be thrown directly.

The underlying implementation of processing method calls

The method #invoke() call depends on the methodaccessor:

// MethodAccessor接口
public interface MethodAccessor {
    /** Matches specification in {@link java.lang.reflect.Method} */
    public Object invoke(Object obj,Object[] args)
        throws IllegalArgumentException,InvocationTargetException;
}

    public Object invoke(Object obj,Object... args)
        throws illegalaccessexception,InvocationTargetException{
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller,Modifier.isStatic(modifiers) ? null : obj.getClass(),modifiers);
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj,args);
    }

The logic for obtaining the methodaccessor instance is similar to that in the previous two sections through reflectionfactory#newmethodaccessor():

    public MethodAccessor newMethodAccessor(Method method) {
        checkInitted();

        if (Reflection.isCallerSensitive(method)) {
            Method altMethod = findMethodForReflection(method);
            if (altMethod != null) {
                method = altMethod;
            }
        }

        // use the root Method that will not cache caller class
        Method root = langReflectAccess.getRoot(method);
        if (root != null) {
            method = root;
        }

        if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
            return new MethodAccessorGenerator().
                generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());
        } else {
            NativeMethodAccessorImpl acc =
                new NativeMethodAccessorImpl(method);
            DelegatingMethodAccessorImpl res =
                new DelegatingMethodAccessorImpl(acc);
            acc.setParent(res);
            return res;
        }
    }

Finally, it will be delegated to nativemethodaccessorimpl#invoke (object obj, object [] args):

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

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

    public Object invoke(Object obj,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.getModifiers());
            parent.setDelegate(acc);
        }

        return invoke0(method,obj,args);
    }

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

    private static native Object invoke0(Method m,Object obj,Object[] args);
}

Nativemethodaccessorimpl#invoke0 () is the native method of the final call of the method call.

Summary

The process of learning knowledge is always stepped up, and the class library design in JDK is similar. If you are familiar with the relevant methods of unsafe class in advance, the underlying implementation of reflection call can also be understood relatively easily. The underlying implementation of property, construction and method reflection calls (only considering normal calls) is as follows:

Personal blog

(end of this paper e-a-20181216 c-1-d)

The official account of Technology (Throwable Digest), which is not regularly pushed to the original technical article (never copied or copied):

Entertainment official account ("sand sculpture"), select interesting sand sculptures, videos and videos, push them to relieve life and work stress.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>