Have to understand the Java class loading mechanism

Java class loading mechanism

Class loading process

There are three main steps for the system to load Class type files: loading -> connection -> initialization . The connection process can be divided into three steps: verification -> preparation -> analysis .

Insert picture description here

load

The first step of the class loading process is to complete the following 3 things:

  1. Obtain the binary byte stream defining this class by the full class name
  2. Convert the static storage structure represented by the byte stream into the runtime data structure of the method area
  3. Generate a Class object representing the class in memory as the access entry for these data in the method area

Part of the content of the loading phase and the connection phase are carried out alternately. The loading phase has not yet ended, and the connection phase may have already begun.

verification

  • File format verification: Mainly verify whether the Class file is standard.
  • Metadata verification: semantic analysis of information described by bytecode, etc.
  • Bytecode verification: Ensure that the semantics are ok.
  • Symbol reference verification: to ensure that the parsing action can be executed.

ready

The preparation stage is the stage of formally allocating memory for the class variable and setting the initial value of the class variable . These memories will be allocated in the method area. At this stage, the following points need to be noted:

  1. At this time, memory allocation only includes class variables (static), not instance variables. Instance variables will be allocated in the Java heap along with the object when the object is instantiated.
  2. The initial value set here "usually" is the default zero value of the data type (such as 0, 0L, null, false, etc.). For example public static int value=111, if we define , then the initial value of the value variable in the preparation stage is 0 instead of 111 ( It will be copied during the initialization phase). Special case: For example public static final int value=111, if the fianl keyword is added to the value variable , then the value of value in the preparation stage is copied to 111.

Parsing

The parsing phase is a process in which the virtual machine replaces the symbol references in the constant pool with direct references. The parsing action is mainly performed on the 7 types of symbol references of classes or interfaces, fields, class methods, interface methods, method types, method handles and call qualifiers.

Symbol reference is a set of symbols to describe the target, which can be any literal. A direct reference is a pointer that directly points to the target, a relative offset, or a handle that is located indirectly to the target. When the program is actually running, only symbol references are not enough. For example, when the program executes a method, the system needs to know where the method is located. The Java virtual machine prepares a method table for each class to store all the methods in the class. When you need to call a method of a class, you can call the method directly as long as you know the offset of the method in the party publication. By parsing the operation symbol reference, it can be directly transformed into the position of the target method in the method table of the class, so that the method can be called.

In summary, the parsing phase is the process of replacing symbol references in the constant pool with direct references by the virtual machine, that is, obtaining pointers or offsets of classes or fields and methods in memory.

initialization

The final step is to initialize class loading, also performs real Java code (bytecode) class defined in the initialization phase is performed class constructor <clinit> ()process approach.

For the <clinit> ()method call, virtual machines themselves to ensure their safety in a multithreaded environment. Because the <clinit>()method is locking thread-safe, so it is class initialization in a multithreaded environment, then may cause a deadlock, and this deadlock is difficult to find.

For the initialization phase, the virtual machine is strictly regulated. There are only 5 cases in which the class must be initialized:

  1. When encountering four direct code instructions of new, getstatic, putstatic, or invokestatic, such as a new class, reading a static field (not final modified), or calling a static method of a class.
  2. Use java.lang.reflectwhen the package method calls the class to reflect, if not the class initialization is required to trigger its initialization.
  3. Initialize a class, if its parent class has not yet been initialized, the initialization of the parent class is triggered first.
  4. When the virtual machine starts, the user needs to define a main class to be executed (the class containing the main method), and the virtual machine initializes this class first.
  5. When using the dynamic language of JDK1.7, if the final analysis structure of a MethodHandle instance is the method handle of REF_getStatic, REF_putStatic, REF_invokeStatic, and the handle is not initialized, you need to trigger the initialization first.

Class loader

Three important ClassLoaders are built into the JVM. All class loaders except BootstrapClassLoader are implemented by Java and inherited from them java.lang.ClassLoader:

  1. BootstrapClassLoader (boot class loader) : top-level class loading, realized by C ++, is responsible for loading %JAVA_HOME%/libjar package and class or directory, or -Xbootclasspathall of the parameters specified in the class path.
  2. ExtensionClassLoader (extension class loader) : primarily responsible for loading the directory %JRE_HOME%/lib/extjar package and class directory, or java.ext.dirsjar package under system variables specified path.
  3. AppClassLoader (application class loader) : A loader for our users, responsible for loading all jar packages and classes under the current application classpath.

After understanding the class loading process and class loader, what we have to understand is the way of class loading: the parent delegation mechanism

Parental delegation mechanism

The process of the parent delegation mechanism is as follows:

Insert picture description here

(1) The current class loader queries whether the class has been loaded from the classes it has loaded, and if it has already been loaded, it will directly return to the originally loaded class.

(2) If it is not found, it will entrust the parent class loader to load it (as shown in the code c = parent.loadClass(name, false)). The parent class loader will also use the same strategy to check whether the class it has already loaded contains this class. If it is, it will return. If not, it will entrust the parent class of the parent class to load it until the class loader is started. Because if the parent loader is empty, it means using the startup class loader as the parent loader to load.

(3) If the startup class loader fails to load (for example, the class is not found in $JAVA_HOME/jre/lib), an exception ClassNotFoundException will be thrown, and then the findClass() method of the current loader will be called to load.

Advantages of the parent delegation model:

(1) Mainly for safety, to avoid dynamically replacing some core classes of Java, such as String, with classes written by users.

(2) At the same time, the repeated loading of classes is avoided, because the JVM distinguishes different classes, not only based on the class name, but the same class file is loaded by different ClassLoader is two different classes.

Insert picture description here

Custom class loader

Custom step

1. Write a class that inherits the ClassLoader abstract class

2. Overwrite his findClass()method

3. findClass()Call in the method defineClass().

ps: here defineClass()is the bytecode converted to Class

Custom ClassLoader

First, we first write a class file for testing,

package ReWriteClassLoaderTest.tt;

public class Apple {
    
    
    public String name;
}

Then compile this class into a class file and put it in a far, far place, I put it here in the root directory of the f drive

Then we build a custom ClassLoader

package ReWriteClassLoaderTest.tt;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;

/**
 * <h3>firstIdeaProject</h3>
 * <p></p>
 *
 * @author : Nicer_feng
 * @date : 2020-09-10 14:57
 **/
public class MyClassLoader extends ClassLoader{
    
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    
        File file = new File("F:/Apple.class");
        //这里放哪了 就写哪里的路径
        try {
    
    
            FileInputStream fileInputStream = new FileInputStream(file);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //每次读取的大小是4kb
            byte[] b = new byte[4 * 1024];
            int n = 0;
            while ((n = fileInputStream.read(b)) != -1) {
    
    
                outputStream.write(b, 0, n);
            }
            //将Apple.class类读取到byte数组里
            byte[] classByteArray = outputStream.toByteArray();
            //调用defineClass 将byte 加载成class
            Class<?> aClass = this.defineClass(name, classByteArray, 0, classByteArray.length);
            return aClass;
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return super.findClass(name);
    }
}

Here we customize the ClassLoader(), let it inherit the ClassLoader, and then we rewrite the findClass() method, so that the rewritten logic is our own specified directory, that is, find the Apple.class file under the f drive and read it Into a byte array, and then call defineClass() to load this class.

Finally, a simple test class Demo

public class Demo {
    
    
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    
    
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> aClass = Class.forName("ReWriteClassLoaderTest.tt.Apple",
                true, myClassLoader);
        Object o = aClass.newInstance();
        System.out.println(o);
        System.out.println(o.getClass().getClassLoader());
    }
}

You can see that there are three parameters in the Class.forName method, the latter two mean true for class reloading, and myClassLoader is our custom class loader

Insert picture description here

Display after running

Insert picture description here

This did not achieve the result we wanted, we still left the AppClassLoader, because our compiler is too smart, directly compile my java file, after the compilation, it is placed under the classpath, we have to go to the target (or out) directory Find the corresponding file and delete it, run again, as follows

Insert picture description here

If you want a deeper understanding, you can go

https://blog.csdn.net/briblue/article/details/54973413

problem

1. Talk about the parental delegation mechanism, why use it?

Parent delegation model process: When a particular class loader receives a request to load a class, it first delegates the loading task to the parent class loader, and then recursively. If the parent class loader can complete the class loading task, it returns successfully; only When the parent class loader is unable to complete this loading task, it loads itself.

Advantages of the parent delegation model:

(1) Mainly for safety, to avoid dynamically replacing some core classes of Java, such as String, with classes written by users.

(2) At the same time, the repeated loading of classes is avoided, because the JVM distinguishes different classes, not only based on the class name, but the same class file is loaded by different ClassLoader is two different classes.

2. Why do you want to customize the class loader?

The simple reason is because the class loader provided by the system cannot meet the actual application of certain scenarios.

Similar to a javaWeb server, multiple websites may be deployed on one server, and each website will use some of the same class libraries. If only the system's class loader is used, only one class library can exist. For different versions of class libraries Can not exist at the same time; JSP hot replacement support, JSP will eventually be compiled into a .class file to be executed by the virtual machine, but the probability of modification during JSP runtime is much greater than that of third-party class libraries or its own .class files, and web applications such as JSP The need to restart after modification is also regarded as a great advantage. The loaders provided by these systems cannot be fully satisfied, and you need to define the loaders yourself.

There are several common answers online:

(1) Encryption: Java code can be easily decompiled . If you need to encrypt your own code to prevent decompilation, you can first encrypt the compiled code with a certain encryption algorithm. After the class is encrypted, you can no longer use Java. The ClassLoader to load the class, then you need to customize the ClassLoader to decrypt the class when loading the class, and then load it.

(2) Load code from non-standard sources: If your bytecode is placed in a database or even in the cloud, you can customize the class loader to load classes from specified sources.

(3) Comprehensive application of the above two situations in practice: For example, your application needs to transmit Java bytecodes through the network. For security, these bytecodes are encrypted. At this time, you need to customize the class loader to read the encrypted byte code from a certain network address, then decrypt and verify, and finally define the class that runs in the Java virtual machine.

3. Is the class loading process of common Java classes the same as that of Tomcat? What's the difference?

see

https://blog.csdn.net/dreamcatcher1314/article/details/78271251

4. What is a class loader

The class loader is to load the class file into the virtual machine, that is, to obtain the binary byte stream describing the class through the fully qualified name of the class.

Class loader is an innovation of Java language, originally designed to meet the needs of Java Applet. Class loaders are currently frequently used in fields such as hierarchical division, hot program deployment, and code encryption.

5. What are the types of class loaders?

JVM provides us with a system class loader (JDK1.8) by default, including:

Bootstrap ClassLoader (system class loader)

Extension ClassLoader

Application ClassLoader (application class loader)

Customer ClassLoader (custom loader)

Guess you like

Origin blog.csdn.net/weixin_43876186/article/details/108514678