Recommended collection! Summary of ultra detailed JVM reflection principle and technical points

Reflection definition

1. The JAVA reflection mechanism is in the running state

For any class, you can know all the properties and methods of this class;

For any object, you can call any of its methods and properties;

This kind of dynamically obtained information and the function of dynamically calling the method of the object are called the reflection mechanism of java language.

Reflection provides the following functions:

  • Determine the class of any object at run time
  • Construct an object of any class at run time
  • Judge the member variables and methods of any class at run time
  • Call the method of any object at run time

(if the attribute is private, external operation of the attribute value is not allowed under normal circumstances. Here, the setAccessible(true) method of Field class can be used to temporarily open the operation permission)

Usage scenario of reflection

  • When Java coding, you know the specific information of classes and objects. At this time, you can operate on classes and objects directly without reflection
  • If you don't know the specific information of the class or object when encoding, you should use reflection to implement it

Reflection source code analysis

Example API:

Class.forName("com.my.reflectTest").newInstance()

1. Get reflection class instance class forName("xxx");

First called Java Lang. class static method to get class information!

Note: the class information obtained by the forName() reflection is not left to java, but to the jvm to load!

The main task is to get the ClassLoader first, and then call the native method to get the information. The loading class is to call back the parameter ClassLoader to load the class!

 @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        // First, get the class information called in through reflection, so as to get the current classLoader
        Class<?> caller = Reflection.getCallerClass();
        // Call the native method to get the class information
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

2. java.lang.ClassLoader-----loadClass()

// java.lang.ClassLoader
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // Get lock first
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // If you have already loaded it, you don't need to load it any more
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // Parental delegate loading
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
 
                // When the parent class is not loaded, it will be loaded by itself
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
 
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    
    protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) {
            // Use ConcurrentHashMap to save locks
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }
    
    protected final Class<?> findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

3. newInstance()

newInstance() In fact, it is equivalent to calling the parameterless constructor of the class,Three main things have been done

  • Permission detection, if not passed, throw an exception directly;

  • Find the parameterless constructor and cache it;

  • Call the parameterless construction method of the specific method, generate an instance and return it;

// The first must be class newInstance
    @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException
    {
        if (System.getSecurityManager() != null) {
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }
 
        // NOTE: the following code may not be strictly correct under
        // the current Java memory model.
 
        // Constructor lookup
        // newInstance() is actually equivalent to calling the class's parameterless constructor, so first find its parameterless constructor
        if (cachedConstructor == null) {
            if (this == Class.class) {
                // Calling newInstance() method of Class is not allowed
                throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {
                // Get parameterless constructor
                Class<?>[] empty = {};
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // Disable accessibility checks on the constructor
                // since we have to do the security check here anyway
                // (the stack depth is wrong for the Constructor's
                // security check to work)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                c.setAccessible(true);
                                return null;
                            }
                        });
                cachedConstructor = c;
            } catch (NoSuchMethodException e) {
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers();
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
        try {
            // Call parameterless constructor
            return tmpConstructor.newInstance((Object[])null);
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }

4. getConstructor0() is the constructor to obtain the matching; There are three steps:

  1. First get all the constructors, and then compare the parameter types;
  2. After finding a match, return a constructor through ReflectionFactory copy;
  3. Otherwise, NoSuchMethodException will be thrown;

private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
        // Get all constructors
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

5. privateGetDeclaredConstructors(), get all the main steps of the constructor;

  1. First try to get from the cache;
  2. If the cache does not exist, it is retrieved from the jvm and stored in the cache. The cache is saved by soft reference to ensure memory availability;

// Get all construction methods of the current class through jvm or cache
    // Returns an array of "root" constructors. These Constructor
    // objects must NOT be propagated to the outside world, but must
    // instead be copied via ReflectionFactory.copyConstructor.
    private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
        checkInitted();
        Constructor<T>[] res;
        // Call reflectionData() to obtain the saved information and save it with soft reference, so that there is not enough memory to recycle
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
            // If there is a cache, return directly
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        if (isInterface()) {
            @SuppressWarnings("unchecked")
            Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
            res = temporaryRes;
        } else {
            // Get the constructor from the jvm using the native method
            res = getDeclaredConstructors0(publicOnly);
        }
        if (rd != null) {
            // Finally, the content read from the jvm is stored in the cache
            if (publicOnly) {
                rd.publicConstructors = res;
            } else {
                rd.declaredConstructors = res;
            }
        }
        return res;
    }
    
    // Lazily create and cache ReflectionData
    private ReflectionData<T> reflectionData() {
        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
        int classRedefinedCount = this.classRedefinedCount;
        ReflectionData<T> rd;
        if (useCaches &&
            reflectionData != null &&
            (rd = reflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
        // else no SoftReference or cleared SoftReference or stale ReflectionData
        // -> create and replace new instance
        return newReflectionData(reflectionData, classRedefinedCount);
    }
    
    // Create a new cache and save the reflection information
    private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                                int classRedefinedCount) {
        if (!useCaches) return null;
 
        // Use cas to ensure the thread safety of updates, so reflection ensures thread safety
        while (true) {
            ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
            // try to CAS it...
            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
                return rd;
            }
            // Use CAS to update first. If the update is successful, return immediately. Otherwise, check whether it has been updated by other threads. If it is consistent with the state you want to update, it is also successful
            oldReflectionData = this.reflectionData;
            classRedefinedCount = this.classRedefinedCount;
            if (oldReflectionData != null &&
                (rd = oldReflectionData.get()) != null &&
                rd.redefinedCount == classRedefinedCount) {
                return rd;
            }
        }
    }

In addition, use relactionData() to save the cache; The data structure of ReflectionData is as follows!

// reflection data that might get invalidated when JVM TI RedefineClasses() is called
    private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;
 
        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;
 
        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }

6. Get the Constructor through the above! Next, just call newInstance() of its corresponding Constructor to return the instance!

// return tmpConstructor.newInstance((Object[])null); 
    // java.lang.reflect.Constructor
    @CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, 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;
    }
    // sun.reflect.DelegatingConstructorAccessorImpl
    public Object newInstance(Object[] args)
      throws InstantiationException,
             IllegalArgumentException,
             InvocationTargetException
    {
        return delegate.newInstance(args);
    }
    // sun.reflect.NativeConstructorAccessorImpl
    public Object newInstance(Object[] args)
        throws InstantiationException,
               IllegalArgumentException,
               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.getParameterTypes(),
                                        c.getExceptionTypes(),
                                        c.getModifiers());
            parent.setDelegate(acc);
        }
 
        // Call the native method to call the constructor
        return newInstance0(c, args);
    }

After returning the instance of the constructor, you can carry out type conversion according to the outside, so as to use the interface or method to call the instance function.

summary

Welcome to pay attention to official account: chengyouguang, get the summary of Java interview questions from first-line large manufacturers + learning and Thinking Guide of each knowledge point + a summary of Java core knowledge points in a 300 page pdf document!
The contents of these materials are the knowledge points that the interviewer must ask during the interview. The chapter includes many knowledge points, including basic knowledge, Java collection, JVM, multithreading concurrency, spring principle, microservice, Netty and RPC, Kafka, diary, design pattern, Java algorithm, database, Zookeeper, distributed cache, data structure, etc.

Tags: Java jvm Programmer

Posted by James138 on Thu, 12 May 2022 06:47:49 +0300