preface

Previously, we have studied the proxy mode, static proxy and dynamic proxy. Dynamic proxy uses Java Reflection to create a new class (also called a “dynamic proxy class”) and its instance (object) at run time that implements some given interface. So it’s worth learning about reflection in Java.

First, basic knowledge

1.1 What is Reflection?

Before I get into reflection, let’s ask a question: Suppose I have a class User and I want to create a User object and get its name attribute. What should I do? User.java

package com.reflect;

/ * * *@author: create by lengzefu
 * @description: com.reflect
 * @date: the 2020-09-29 * /
public class User {
    private String name = "Xiao Ming";
    
    Integer age = 18;
    
    public User(a){}public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge(a) {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age; }}Copy the code

The way is simple:

import com.reflect.User;

public class Main {

    public static void main(String[] args) {
        User user = newUser(); System.out.println(user.getName()); }}Copy the code

This is a common way to write code. This is because when we use an object, we always know in advance which class we need to use, so we can use the direct new method to get the object of the class and then call the methods of the class. What if we didn’t know in advance what class we were going to use? This is a very common scenario, such as dynamic proxies, where we don’t know what the proxy class is in advance and the proxy class is generated at run time. In this case, we use today’s main topic: reflection

1.1.1 Definition of Reflection

The JAVA reflection mechanism means that at runtime, all properties and methods of any class are known. You can call any method or property of any object. This ability to dynamically retrieve information and dynamically invoke methods on objects is called the Reflection mechanism of the Java language. Note: Special emphasis is placed on the running state.

1.2 What can reflection do?

The definition already gives us the answer. Reflection allows a program to take any property or method of any class and call it at run time.

1.3 Why can reflection be done?

Here we need to introduce the concept of “class objects”. The concept of “object oriented” in Java is thoroughly implemented, and it emphasizes that “everything is an object”. Can a “class” also be considered an object? Java has a special Class: Class, whose objects are “classes,” such as “String” and “Thread”.

Java.lang. Class is the interface to access type metadata, which is the key data to implement reflection. Through the interface provided by Class, you can access a type’s methods, fields, and so on.

The answer to the question of why reflection makes it possible for a program to retrieve and call any property or method of any class at runtime is that it relies on.class bytecode files to do so. So the first problem we need to solve is how to get the bytecode file object (the Class object).

1.3.1 Obtaining Class Objects

What do I do for a Class, such as User above, and I want to get information about the User (since Users are objects of Class, this action is generally called “getting Class objects”)? There are three ways

import com.reflect.User;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        // 1. The instantiated Object can be obtained by calling getClass(). This method is usually used when passing an Object of type Object
        User user = new User();
        Class clz1 = user.getClass();

        // 2. This method is the most secure and reliable method for program performance, which indicates that every class has an implicit static member variable class
        Class clz2 = User.class;

        // Get the Class object from the full path name of the Class. This is most commonly used. If the Class cannot be found according to the classpath, then a ClassNotFoundException will be thrown.
        Class clz3 = Class.forName("com.reflect.User");

        Clz1 = clz2; clz3 = clz1; clz2 = clz3;System.out.println(clz1.equals(clz2)); System.out.println(clz2.equals(clz3)); }}Copy the code

1.3.2 Class API

GetDeclaredConstructors () Gets all constructors. GetDeclaredConstructors () gets the class object. NewInstance () gets the class name, including the package path GetSimpleName () gets all the properties of the public type of the class. GetFields () gets all the properties of the class. GetDeclaredFields () gets the specified properties of the public type of the class GetDeclaredField (String name) for class public type of method getMethods () get all the methods of class getDeclaredMethods () to obtain such specific types of public methods: getMethod(String name, Class[] parameterTypes) Get getDeclaredClasses() get getDeclaringClass() get getModifiers() Get getPackage() Get the implemented interface getInterfaces()Copy the code

How to use specific no longer tautology

Second, the reflection principle analysis

2.1 The relationship between reflection and class loading

The execution of a Java class goes through the following process,

Compile: Java files are generated after compilation. Class bytecode files are loaded: The classloader is responsible for reading the binary byte stream of a Class based on its fully qualified name into the JVM, storing it in the method section of the runtime memory area, and then converting it to a java.lang.Class object instance link corresponding to the target type:

  • Validation: format (class file specification) semantics (whether final classes have subclasses) operations
  • Preparation: Static variables are assigned initial values and memory space, and final decorated memory space is directly assigned original values, not user-specified initial values here.
  • Parse: symbolic references are converted to direct references, and addresses are assigned

Initialization: a parent class initializes its parent class, then initializes itself; If it is a static variable, override the original value with the user-specified value. If it is a code block, the operation is performed once.

Java reflection operates by using the.class file loaded into the JVM in step 2 above. The second step is to load the.class file in the JVM to operate. The.class file contains all the information about a Java class. When you don’t know the specific information about a class, you can use reflection to retrieve the class and then perform various operations.

First let’s look at how to use reflection to implement method calls:

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 1. Obtain the class object
        Class clz3 = Class.forName("com.reflect.User");
        // 2. Get the constructor of the class
        Constructor constructor = clz3.getConstructor(String.class, Integer.class);
        // 3. Create an object
        User user = (User)constructor.newInstance("Life".17);
        // 4. Obtain method getName
        Method method = clz3.getMethod("getName");
        // 5. Call the methodString name = (String) method.invoke(user); System.out.println(name); }}Copy the code

The following two processes are mainly resolved: 4 and 5: get Method object and methode.invoke

2.2 Obtaining a Method Object

2.2.1 Obtaining the Method API

The Class API provides the following methods for obtaining a Method object: GetMethod/getMethods and getDeclaredMethod/getDeclaredMethod suffixes have said the difference between “s” with “s” for all, without “s” is said to obtain specific () designated by the method parameters. GetMethod = DECLARED; getMethod = DECLARED; DeclaredMethod = DECLARED; getMethod = DECLARED; DeclaredMethod = DECLARED; getMethod = DECLARED; DeclaredMethod = DECLARED

public
interface Member {

    /** * Identifies the set of all public members of a class or interface, * Including inherited Members. * Identifies a collection of all public members of a class or interface, including the parent class's public members. * /
    public static final int PUBLIC = 0;

    /** * Identifies the set of declared members of a class or interface. * Inherited members are not included. * A collection (public, protected,private) that identifies all declared members of a class or interface, but does not include superclass members */
    public static final int DECLARED = 1;
}
Copy the code

In fact whether getMethod or getDeclaredMethod, underlying all call the same method: privateGetDeclaredMethods, therefore we only one method.

2.2.2 Source code analysis of getMethod

seq1
// 4. Obtain method getName
Method method = clz3.getMethod("getName");
Copy the code

The client calls the class.getMethod () method.

seq2
	// The parameter "name" is the name of the method, and the parameter "parameterTypes" is the parameter of the method. There may be multiple parameters of different types, so the generic and variable parameter Settings are used here
    public Method getMethod(String name, Class
       ... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        // If no permission is granted, a SecurityException will be thrown
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        // If the obtained method is null, NoSuchMethodException is thrown
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
Copy the code

The core method of this method is getMethod0.

seq3
private Method getMethod0(String name, Class<? >[] parameterTypes,boolean includeStaticMethods) {
		// Save the method in the interface, at most 1, but MethodArray initialization size at least 2
        MethodArray interfaceCandidates = new MethodArray(2);
        GetMethod () ¶ getMethod () ¶ getMethod (); getMethod ()
        Method res =  privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
        // Get a method from this class or its parent class and return the result directly
        if(res ! =null)
            return res;

        // Not found on class or superclass directly
        // If no corresponding method is found in this class or its parent class, try to find a method in the interface
        // removeLessSpecifics: Remove the less specific methods and preserve the methods with a more specific implementation
        interfaceCandidates.removeLessSpecifics();
        return interfaceCandidates.getFirst(); // may be null
    }
Copy the code

The following analysis privateGetMethodRecursive

seq4
 private Method privateGetMethodRecursive(String name, Class<? >[] parameterTypes,boolean includeStaticMethods,
            MethodArray allInterfaceCandidates) {
        // Must _not_ return root methods
        Method res;
        // declare declared public methods
        if ((res = searchMethods(privateGetDeclaredMethods(true), name, parameterTypes)) ! =null) {
            if(includeStaticMethods || ! Modifier.isStatic(res.getModifiers()))// if res is not empty, return
                return res;
        }
        // Search superclass's methods res is empty
        if(! isInterface()) {// Interfaces must have no superclasses
            Class<? super T> c = getSuperclass();
            if(c ! =null) {
	            // Call getMethod0 recursively to get the method implementation of the parent class
                if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
                    returnres; }}}// Search superinterfaces' methods resClass<? >[] interfaces = getInterfaces();for(Class<? > c : interfaces)// Call getMethod0 recursively to get the method implementation of the interface
            if ((res = c.getMethod0(name, parameterTypes, false)) != null)
                allInterfaceCandidates.add(res);
        // Not found
        return null;
    }
Copy the code

The approximate meaning of this method after translation from the original English annotation is:

Note: The purpose of the search algorithm used by this routine (which acts like a function but has more meaning than a function) is to be equivalent to the order in which the privateGetPublicMethods methods are applied. It only gets the declared public methods for each class, however, to reduce the number of Method objects that would normally have to be created if the requested Method was declared in the class to be queried. Since default methods are used, methods declared in any superinterface need to be considered unless they are found on the superclass. All candidates declared in the superinterface of all InterfaceCandidates are collected, and if no match is found on the superclass, the most specific candidate is selected.

The original English notes in a variety of clauses really many, their translation feeling is still a bit of a problem. In short, I think the important point is to get the declared method of the class, and if the default method is used, look for that method from the parent class. Otherwise, go to the interface and look for the candidate method with the most specific implementation

Next, look at searchMethods

seq5
    private static Method searchMethods(Method[] methods, String name, Class
       [] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName // Compare method names
	            // Compare method parameters
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                // The value returned by the comparison method
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }
Copy the code

The implementation logic of searchMethods is simple and detailed, such as annotations. The key here is how the Method parameter [] methods is obtained. Let’s go back to the Method call to searchMethods:

searchMethods(privateGetDeclaredMethods(true), name, parameterTypes)) ! =null)
Copy the code

The methods through the methods privateGetDeclaredMethods (true)

seq6
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
        checkInitted();
        Method[] res;
        // 1.ReflectionData Cache structure that stores reflected data
        ReflectionData<T> rd = reflectionData();
        if(rd ! =null) {
	        // 2. Retrieve methods from cache
            res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
            if(res ! =null) return res;
        }
        // No cached value available; request value from VM
        // 3. No cache, get it through JVM
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
        if(rd ! =null) {
            if (publicOnly) {
                rd.declaredPublicMethods = res;
            } else{ rd.declaredMethods = res; }}return res;
    }
Copy the code

First take a look at ReflectionData < T >

 // 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;
        volatileClass<? >[] interfaces;// Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount; }}Copy the code

ReflectionData

is a static inner Class of Class Class.

represents a generic type and is a concrete Class object. This cache data structure stores all the information about the class. RedefinedCount is the number of times a class has been redefined and can be understood as the cached version number. Reflection data that might get invalidated when the JVM TI RedefineClasses() is called. This means that the cached data may be invalidated when the JVM TI RedefineClasses() is called.

From the above analysis, we know that every class object theoretically has a cache of ReflectionData

, so how to get it?

That’s where reflectionData comes in

    // Lazily create and cache ReflectionData
    private ReflectionData<T> reflectionData(a) {
	    // Get the current reflectionData cache
        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
        // The current cache version number
        int classRedefinedCount = this.classRedefinedCount;
        ReflectionData<T> rd;
        // Cache can be used && The cache is not empty && the version number in the cache is the same as the version number recorded in the class
        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
        // Create new cache data
        return newReflectionData(reflectionData, classRedefinedCount);
    }
Copy the code

Look at the implementation of newReflectionData

    private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                                int classRedefinedCount) {
        // Return null without caching
        if(! useCaches)return null;

		// Create a new ReflectionData and return it if the update is successful
        while (true) {
            ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
            // try to CAS it...
            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
                return rd;
            }
            // else retry
            // Get the old reflectionData and classRedefinedCount values. If the old reflectionData and classRedefinedCount values are not null and the cache is not invalid, then the update is successful
            oldReflectionData = this.reflectionData;
            classRedefinedCount = this.classRedefinedCount;
            if(oldReflectionData ! =null&& (rd = oldReflectionData.get()) ! =null &&
                rd.redefinedCount == classRedefinedCount) {
                returnrd; }}}Copy the code

We’re now going back to privateGetDeclaredMethods method implementation for step 3:

  // 3. No cache, get it through JVM
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
Copy the code

A native method is called, so I won’t repeat it here. After obtaining the corresponding method, it will not return directly, as follows:

 return (res == null ? res : getReflectionFactory().copyMethod(res));
Copy the code
seq7

Step through getReflectionFactory().copymethod (res) calls Method#copy

    Method copy(a) {
	    // 1. If the root value of the object is NULL, it indicates that the object is a basic method object
        if (this.root ! =null)
	        // 2. Copy only basic method objects, that is, objects whose root is NULL
            throw new IllegalArgumentException("Can not copy a non-root Method");
		// 3. Create a method object that has the same properties as the basic method object
        Method res = new Method(clazz, name, parameterTypes, returnType,
                                exceptionTypes, modifiers, slot, signature,
                                annotations, parameterAnnotations, annotationDefault);
        // 4. Res is a copy of this, and its root attribute must point to this
        res.root = this;
        // Might as well eagerly propagate this if already present
        // 5. All copies of methods will use the same methodAccessor
        res.methodAccessor = methodAccessor;
        return res;
    }
Copy the code

Note the following:

  • The root attribute means that every Java Method has a unique Method object. This object is called root and is invisible to the user. This root is not exposed to the user. When we retrieve a Method object from reflection, we create a Method object that wraps root around and gives it to the user. Every Method object we obtain in our code is a copy (or reference) of it.
  • MethodAccessor: The root object holds a methodAccessor object, so all acquired Method objects share this methodAccessor object, so it must be made visible in memory.
  • res.root = this: res is a copy of this, and its root attribute must point to this.
summary

GetMethod Method sequence diagram

2.3 Method. Invoke

    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if(! override) {// 1. Check permissions
            if(! Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<? > caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); }}// 2. Obtain MethodAccessor
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
	        // create MethodAccessor when 2.1 is empty
            ma = acquireMethodAccessor();
        }
        // 3. Call MethodAccessor.invoke
        return ma.invoke(obj, args);
    }
Copy the code

2.3.1 Checking permissions

So we’re judging the override variable, if override == true, we skip the check and we usually call Method#setAccessible(true) before Method#invoke, Set the override value to true.

2.3.2 get MethodAccessor

We talked about when we got Method above, Method#copy assigns a value to the Method accessor of the Method. So methodAccessor in this case is the methodAccessor that was used for the copy. If ma is empty, create a MethodAccessor.

    /* Note that we don't use synchronization. It is correct (though inefficient) to generate more than one MethodAccessor for a given method. However, avoiding synchronization may make the implementation more scalable. * /
    private MethodAccessor acquireMethodAccessor(a) {
        // First check to see if one has been created yet, and take it
        // if so
        MethodAccessor tmp = null;
        if(root ! =null) tmp = root.getMethodAccessor();
        if(tmp ! =null) {
            methodAccessor = tmp;
        } else {
            // Otherwise fabricate one and propagate it up to the root
            tmp = reflectionFactory.newMethodAccessor(this);
            setMethodAccessor(tmp);
        }

        return tmp;
    }
Copy the code

So this is going to look for root’s MethodAccessor, which was set in Method#copy. If you still don’t find it, you create a MethodAccessor.

class ReflectionFactory {
    public MethodAccessor newMethodAccessor(Method method) {
        // noInflation is assigned
        checkInitted();
        // ...
        if(noInflation && ! ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {// Generates MethodAccessorImpl
            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);
            returnres; }}}Copy the code

As you can see here, there are three methods Accessors. MethodAccessorImpl NativeMethodAccessorImpl, DelegatingMethodAccessorImpl. NoInflation defaults to false. Only if sun.reflect.noInflation is true. MethodAccessorImpl is used. So NativeMethodAccessorImpl is called by default.

MethodAccessorImpl is a method call made by dynamically generating bytecode, which is the Java version of MethodAccessor, but bytecode generation is a bit more complicated, so I won’t put the code in here. You can see the generate method here.

DelegatingMethodAccessorImpl is pure agent, the real implementation or NativeMethodAccessorImpl.

class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
    private MethodAccessorImpl delegate;
 
    DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
        setDelegate(delegate);
    }
 
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        return delegate.invoke(obj, args);
    }
 
    void setDelegate(MethodAccessorImpl delegate) {
        this.delegate = delegate; }}Copy the code

NativeMethodAccessorImpl is the Native implementation of MethodAccessor.

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())) {// The Java version of MethodAccessor
            MethodAccessorImpl acc = (MethodAccessorImpl)
                new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),
                                   method.getName(),
                                   method.getParameterTypes(),
                                   method.getReturnType(),
                                   method.getExceptionTypes(),
                                   method.getModifiers());
            parent.setDelegate(acc);
        }
        // Native version call
        return invoke0(method, obj, args);
    }
 
    private static native Object invoke0(Method m, Object obj, Object[] args);
}
Copy the code

In the implementation of NativeMethodAccessorImpl, we can see that there is a threshold control for numInvocations, which represents the number of calls. If numInvocations are greater than 15 (the default threshold is 15), the Java version of MethodAccessorImpl is used. Why do you use this strategy? You can use the comments in the JDK:

// "Inflation" mechanism. Loading bytecodes to implement
Copy the code
// Method.invoke() and Constructor.newInstance() currently costs // 3-4x more than an invocation via native code for the  first // invocation (though subsequent invocations have been benchmarked // to be over 20x faster). Unfortunately this cost increases // startup time for certain applications that use reflection // intensively (but only once per class) to bootstrap themselves. // To avoid this penalty we reuse the existing JVM entry points // for the first few invocations of Methods and Constructors and // then switch to the bytecode-based implementations. // // Package-private to be accessible to NativeMethodAccessorImpl // and NativeConstructorAccessorImpl private static boolean noInflation = false;Copy the code

The Java version calls MethodAccessorImpl more than 20 times faster than the Native version, but the Java version uses 3-4 times more resources to load than the Native version, so the default is to call the Native version. After more than 15 calls, the more efficient version of Java is chosen. So why is the Native version less efficient than the Java version? From R big blog, because this is the HotSpot way of optimizing the performance characteristics, as well as many virtual machines have in common: across the border from native will hinder for optimization, it’s like a black box let the virtual machine is difficult to analysis will also be the inline, and running after a long time after it was hosted version of the code faster.

2.3.3 calling MethodAccessor#invoke implements method calls

After the MethodAccessor is generated, its invoke method is called for the final reflection call. Here we do a simple analysis of the Java version of MethodAccessorImpl, not the Native version. Earlier we mentioned that MethodAccessorImpl generates dynamic bytecode using MethodAccessorGenerator#generate and loads it dynamically into the JVM. This is basically the end of the Java method reflection principle.

Three, why is the reflection slow?

3.1 Why is it slow?

The Java version takes more time to initialize, but performs better over time. The native version, on the other hand, is relatively fast at startup, but over time it’s no better than the Java version. This is a performance feature of HotSpot’s optimization approach, which is common to many virtual machines: crossing native boundaries can be a barrier to optimization, it’s a black box that makes it hard for the virtual machine to analyze and inline, and the managed version of the code runs faster over time. To balance the performance of the two versions, Sun’s JDK uses the technique of “Inflation” : When the number of reflected calls exceeds the threshold, a dedicated MethodAccessor implementation class is generated that generates the bytecode of the Invoke () method. Subsequent reflected calls to the Java method will use the Java version. When the reflection call becomes a hotspot, it can even be inlined to the side near Method.invoke(), greatly reducing the overhead of the reflection call. The native version of reflection calls cannot be effectively inlining, so the call overhead cannot be reduced as the program runs.

In summary, the reasons are as follows:

  1. Jit cannot optimize reflection. JIT compilers do not effectively optimize reflection, as explained in a Java doc:

Some Java virtual machine optimizations cannot be performed because reflection involves dynamically resolved types. Therefore, the performance of reflection operations is slower than that of non-reflection operations, so you should avoid using reflection in performance-sensitive applications

  1. Parameter encapsulation/decapsulation and method verification. The invoke method is of type Object. If it is a simple type such as long, it must be encapsulated as Object at the interface. Thus, a large number of objects of long are generated, resulting in additional unnecessary memory waste and possibly even GC. Visibility verification of the parameter checksum method is required.
  2. It is difficult to inline

3.2 Slow reflection why use it?

Reflection is a technique that is widely used in framework design, but there is a performance cost to reflection, so why use it?

  • The performance bottleneck of most systems is far from enough to consider reflection. Here, optimizations at the logical and data layers can improve performance by n orders of magnitude more than optimizations for reflection.
  • The design of a framework is a trade-off between performance, standards, and development efficiency.
  • There’s a performance cost to reflection, but it’s generally negligible, and Java’s support for reflection in javabeans, which is basically PropertyDescriptor and MethodDescriptor support, reduces reflection cost to a certain extent. With regard to AOP, CGLIb is operated by generating subclasses of the bytecode of the class. Once subclasses are generated, they are purely reflection calls that no longer operate bytecode, whereas general AOP calls are on singletons and do not subclass cGLIb frequently.

For the details of reflection performance test data, may refer to: www.jianshu.com/p/4e2b49fa8… In fact, it can be seen from the above data that when the equivalent is very large, reflection does affect performance. But ordinary applications, even without the help of a high-performance toolkit, are not constrained. Of course, this does not mean that it can be used at will, or combined with practical applications.

Four, the advantages and disadvantages of reflection

4.1 Advantages of reflection

  • Gives programs the ability to manipulate objects at run time.
  • Decouple, improve the extensibility of the program.

4.2 Disadvantages of reflection

Performance overhead reflection involves dynamic parsing of types, so the JVM cannot tune this code. Therefore, reflective operations are much less efficient than those that are not reflective. We should avoid using reflection in frequently executed code or in programs that require high performance. Security restrictions Using reflection techniques requires that the program be run in an environment without security restrictions. This is a problem if a program must be run in an environment with security restrictions, such as an Applet. Because reflection allows code to do things that it would not normally be allowed to do (such as access private properties and methods), using reflection can lead to unintended side effects — code with functional errors that reduce portability. Reflecting code breaks abstraction, so as the platform changes, the behavior of the code is likely to change as well.

4.3 the principle of

If you can do it using normal methods, don’t use reflection.

5. References

www.jianshu.com/p/607ff4e79… www.cnblogs.com/chanshuyi/p… Blog.csdn.net/zhenghongcs…

The last

  • If you feel there is a harvest, three consecutive support;
  • If there are mistakes in the article, please comment and point out, also welcome to reprint, reprint please indicate the source;
  • Personal VX: Listener27, exchange technology, interview, study materials, help the first-line Internet manufacturers in the promotion, etc