Class loading subsystem

Overview of Memory structure

brief

Detailed figure

The English version

The Chinese version of

Note: only HotSpot virtual machine has method area, J9, JRockit does not

If you want to write a Java virtual machine by hand, what are the main structures to consider?

  1. Class loader
  2. Execution engine

Class loader subsystem

Class loader subsystem functions:

  1. The classloader subsystem is responsible for loading Class files from the file system or network. Class files have specific file identifiers at the beginning of the file.

  2. The ClassLoader is only responsible for loading the class file, and whether it can run is determined by the Execution Engine.

  3. The loaded class information is stored in a piece of memory called the method area. In addition to Class information, the method area stores runtime constant pool information, possibly including string literals and numeric constants (this constant information is a memory map of the constant pool portion of the Class file).

ClassLoader ClassLoader role

  1. The class file (Car. Class file in the image below) exists on the local hard disk and can be interpreted as a template drawn on paper by the designer, which is eventually loaded into the JVM to instantiate n identical instances from this file.

  2. The class file is loaded into the JVM, called the DNA metadata template (in the image below, Car class in memory), and placed in the method area.

  3. In the.class file – >JVM – > eventually becoming the metadata template, this process requires a transport (class Loader) that acts as a Courier.

Class loading process

public class HelloLoader {

    public static void main(String[] args) {
        System.out.println("Thank you ClassLoader for loading me....");
        System.out.println("Your great kindness, I will report again in the next life!");
    }
}` </pre>
Copy the code

How does it load?

  • To execute the main() method (static), you first need to load the main class HelloLoader

  • If the load is successful, the link and initialization operations are performed. When done, call the static method main in the HelloLoader class

  • An exception is thrown if the load fails

The complete flow chart is as follows:

Loading stage

Loading:

  1. Gets the binary byte stream that defines a class by its fully qualified name.

  2. Transform the static storage structure represented by this byte stream into the runtime data structure of the method area.

  3. Generate a java.lang.Class object in memory that represents the Class and acts as an access point for the Class’s various data in the method area.

Loading class files:

  1. Load directly from the local system.

  2. This parameter is obtained over the network. The typical scenario is a Web Applet.

  3. Read from zip package, become the future jar, WAR format basis.

  4. Runtime computational generation, most commonly used: dynamic proxy technology.

  5. Generated by other files. Typical scenario: JSP applications extract. Class files from a proprietary database, which is rare.

  6. Obtained from encrypted files, a typical protection against Class files being decompiled.

Link phase

Links are divided into three sub-stages: validate -> prepare -> parse

Validation (Verify)

  1. To ensure that the byte stream in the Class file meets the requirements of the CURRENT VM, ensure that the loaded Class is correct and does not harm vm security
  2. It mainly includes four kinds of verification, file format verification, metadata verification, bytecode verification, symbol reference verification.

For example,

Use BinaryViewer to view the bytecode files, which all start with CAFE BABE. If there is an invalid bytecode file, the verification will fail.

To Prepare (Prepare)

  1. Allocates memory for class variables (static variables) and sets the default initial value of the class variable, which is zero.

  2. Static with final is not included here, because final assigns a default value at compile time and is explicitly initialized during the prep phase.

  3. Note: there is no initialization for instance variable allocation, class variables are allocated in the method area, and instance variables are allocated in the Java heap along with the object.

For example,

Code: The variable a is assigned an initial value during the preparation phase, but instead of 1, it is assigned a value of 0 during the initialization phase.

public class HelloApp {
    private static int a = 1;//prepare:a = 0 ---> initial : a = 1

    public static void main(String[] args) {
        System.out.println(a);
    }
}` </pre>
Copy the code

Resolution (Resolve)

1. The process of converting symbolic references in a constant pool to direct references.

2. In fact, parsing operations are often performed with the JVM after initialization.

3. A symbolic reference is a set of symbols that describe the referenced object. The literal form of symbolic references is clearly defined in the Class file format of the Java Virtual Machine Specification. A direct reference is a pointer to a target directly, a relative offset, or a handle to the target indirectly.

4. Parse actions are mainly for classes or interfaces, fields, class methods, interface methods, and method types. CONSTANT Class Info, CONSTANT Fieldref Info, and CONSTANT Methodref Info in the CONSTANT pool.

Symbolic reference

  • After decompiling the class file, you can look at symbolic references, which are marked with # below

Initialization phase

Class initialization timing

  1. Create an instance of the class

  2. Accesses or assigns a value to a static variable of a class or interface

  3. Call a static method of a class

  4. Reflection (e.g. Class.forname (” com.atguigu.test “))

  5. Initialize a subclass of a class

  6. Classes that are identified as startup classes when the Java virtual machine starts

  7. JDK7 began to offer a dynamic language support: Java. Lang. Invoke. Analytical results REF_getStatic MethodHandle instance, REF putStatic, REF_invokeStatic handle corresponding class does not initialize, is initialized

In all but seven cases, the use of Java classes is considered passive and does not result in class initialization, that is, the initialization phase (the clinit() and init() methods are not called).

clinit()

  1. The initialization phase is the execution of the class constructor method

    ()

  2. This method does not need to be defined and is a combination of the statements in the static code block and the assignment actions of all class variables that the Javac compiler automatically collects in a class. That is, when we include static variables in our code, we have the Clinit method

  3. The instructions in the

    () method are executed in the order in which the statements appear in the source file


  4. () is different from the constructor of the class. (Correlation: constructor is

    () from the virtual machine perspective)

  5. If the class has a parent class, the JVM guarantees that

    () of the parent class has been executed before

    () of the subclass is executed

  6. The virtual machine must ensure that a class’s

    () method is locked synchronously in multiple threads

IDEA install JClassLib Bytecode Viewer plug-in, can be very convenient to see Bytecode. The installation process can be self-contained

1,2,3

Example 1: Have static variables

Looking at the bytecode of the code below, you can see that there is one<clinit>()Methods.

public class ClassInitTest {
   private static int num = 1;

   static{
       num = 2;
       number = 20;
       System.out.println(num);
       //System.out.println(number); // Error: invalid forward reference.
   }

   /** * 1, linking: number = 0 --> 20 --> 10 * 2, the static code block appears before the declaration variable, so the number variable, which was set to 0, is initialized to 20 and then initialized to 10
   private static int number = 10;

    public static void main(String[] args) {
        System.out.println(ClassInitTest.num);/ / 2
        System.out.println(ClassInitTest.number);/ / 10
    }
}` </pre>
Copy the code

< Clint bytecode >:

0 iconst_1
 1 putstatic #3 <ClassInitTest.num>
 4 iconst_2
 5 putstatic #3 <ClassInitTest.num>
 8 bipush 20
10 putstatic #5 <ClassInitTest.number>
13 getstatic #2 <java/lang/System.out>
16 getstatic #3 <ClassInitTest.num>
19 invokevirtual #4 <java/io/PrintStream.println>
22 bipush 10
24 putstatic #5 <ClassInitTest.number>
27 return` </pre>
Copy the code

When we include static variables in our code, we have the Clinit method

Example 2: There is no static variable

And then you add it

4. Illustrate

In the constructor:

  • We start by assigning the class variable a a value of 10
  • Assign the local variable to 20

5 illustrates

If the class has a parent class, the JVM guarantees subclasses<clinit>()Before execution, the parent class<clinit>()The execution is complete.

The loading process is as follows:

  • First, executing the main() method requires the ClinitTest1 class to be loaded

  • To get the son. B static variable, load the Son class

  • The parent of the Son class is Father, so you need to load the Father class first and then the Son class

6 explain

The virtual machine must ensure that a class’s

() method is locked synchronously in multiple threads

public class DeadThreadTest {
    public static void main(String[] args) {
        Runnable r = () -> {
            System.out.println(Thread.currentThread().getName() + "Start");
            DeadThread dead = new DeadThread();
            System.out.println(Thread.currentThread().getName() + "The end");
        };

        Thread t1 = new Thread(r,Thread 1 "");
        Thread t2 = new Thread(r,Thread 2 ""); t1.start(); t2.start(); }}class DeadThread{
    static{
        if(true){
            System.out.println(Thread.currentThread().getName() + "Initialize current class");
            while(true){

            }
        }
    }
}` </pre>
Copy the code

Output result:

thread2To start a thread1To start a thread2</pre> Initialize current class/then program freezesCopy the code

Program stuck, analyze the cause:

  • Both threads load the DeadThread class at the same time, and the DeadThread class has a kill loop in its static code block

  • The thread that loaded the DeadThread class first grabbed the lock and then executed an infinite loop in the static code block of the class while the other thread waited for the lock to be released

  • So no matter which program loads the DeadThread class first, the other class doesn’t continue. (A class is loaded only once)

Classification of class loaders

An overview of the

  1. The JVM strictly supports two types of classloaders. Bootstrap ClassLoader and User-defined ClassLoader respectively

  2. A custom ClassLoader is conceptually a class of class loaders defined by a developer in a program, but the Java Virtual Machine specification does not define it this way. Instead, it classifies all classloaders derived from the abstract ClassLoader as custom classloaders

  3. No matter how classloaders are classified, the most common class loaders in our programs are always only three, as shown below

public class ClassLoaderTest {
    public static void main(String[] args) {

        // Get the system class loader
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        // Get its upper layer: extension class loader
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d

        // The boot class loader is not available
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);//null

        // For user-defined classes, the system class loader is used for loading by default
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        // The String class is loaded using the bootstrap classloader. --> Java's core class libraries are loaded using the boot class loader.
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);//null

    }
}` </pre>
Copy the code
  • We tried to get the bootloader and got a value of null. This does not mean that the bootloader does not exist, because the bootloader is in C/C++ language and we cannot get it.
  • Both get the same value for the system classloader: sun.misc.Launcher$AppClassLoader@18b4aac2, indicating that the system classloader is globally unique.

A built-in loader of the VM

Start the class loader

Start the ClassLoader (Bootstrap ClassLoader)

  1. This class loading is implemented in C/C++ and is nested within the JVM

  2. It is used to load Java’s core libraries (JAVA_HOME/jre/lib/rt.jar, resources.jar, or sun.boot.class.path) to provide classes that the JVM itself needs

  3. Does not inherit from java.lang.ClassLoader and has no parent loader

  4. Load extension classes and application class loaders as their parent class loaders

  5. For security reasons, Bootstrap starts the class loader to load only classes whose package names start with Java, Javax, and Sun

Extend the class loader

Extension ClassLoader

  1. Java language, implemented by sun.misc.Launcher$ExtClassLoader

  2. Derived from the ClassLoader class

  3. The parent class loader is the initiator class loader

  4. Load the class libraries from the directory specified by the java.ext.dirs system property, or from the JRE /lib/ext subdirectory (extension directory) of the JDK installation directory. If user-created jars are placed in this directory, the system class loader is automatically loaded by the extension class loader as well

System class loader

Application class loader (also known as system class loader, AppClassLoader)

  1. Java language by sun. Misc. LaunchersAppClassLoader implementation

  2. Derived from the ClassLoader class

  3. The parent class loader is the extension class loader

  4. It is responsible for loading the class libraries under the path specified by the environment variable classpath or the system property java.class.path

  5. Class loading is the default class loader in a program, and it is generally used to load classes in Java applications

  6. Through this. GetSystemclassLoader () method can get the class loader

public class ClassLoaderTest1 {
    public static void main(String[] args) {
        System.out.println("********** Start class loader **************");
        // Get the API path that BootstrapClassLoader can load
        URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urLs) {
            System.out.println(element.toExternalForm());
        }
        // Select a class from the path above and see what its classloader is: bootstrap classloader
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);

        System.out.println("*********** Extended class loader *************");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")) {
            System.out.println(path);
        }

        // Select a class from the path above and see what its classloader is: extension classloader
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19d

    }
}` </pre>
Copy the code

Output result:

'********** launches the classloader ************** file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131/jre/lib/jfr.jar
file:/C:/Program%20Files/Java/jdk18.. 0_131 / jre/classes null * * * * * * * * * * * the extension class loader * * * * * * * * * * * * * C: \ Program Files \ Java \ jdk18.. 0_131\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
sun.misc.Launcher$ExtClassLoader@29453f44` </pre>
Copy the code

User-defined class loaders

When do I need a custom class loader?

In everyday Java application development, the loading of classes is almost carried out by the above three types of loaders together. If necessary, we can also customize the class loaders to customize the way the class is loaded. So why do you need a custom classloader?

  1. Isolation of loaded classes (for example, if I assume that the Spring framework and RocketMQ now have classes with exactly the same package name and path, and the same class name, then the classes conflict. However, most mainstream frameworks and middleware have custom class loaders that implement different frameworks and are isolated from each other.

  2. Modify the way classes are loaded

  3. Extend the load source (you can also consider loading classes from databases, routers, etc.)

  4. Prevent source leakage (decrypt bytecode files and use custom class loaders to decrypt them for your own use)

How do I customize class loaders?

  1. Developers can implement their own classloaders by inheriting the abstract java.lang.ClassLoader class to meet specific needs

  2. Before JDK1.2, it was common to inherit the ClassLoader class and rewrite the loadClass() method to implement custom classloading classes. However, after JDK1.2, it is no longer recommended to override the loadClass() method. Instead, I suggest that you write your custom classloading logic in the findClass () method

  3. When writing a custom class loader, if you don’t have too complicated requirements, you can inherit the URIClassLoader class directly. This way, you can avoid writing the findClass () method and how to get the bytecode stream yourself, making the custom class loader writing simpler.

public class CustomClassLoader extends ClassLoader {
    @Override
    protectedClass<? >findClass(String name) throws ClassNotFoundException {

        try {
            byte[] result = getClassFromCustomPath(name);
            if (result == null) {
                throw new FileNotFoundException();
            } else {
                //defineClass is used with findClass
                return defineClass(name, result, 0, result.length); }}catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        throw new ClassNotFoundException(name);
    }
	// Customize how to get the stream
    private byte[] getClassFromCustomPath(String name) {
        // Load the specified class from the custom path: details are omitted
        // If the bytecode file at the specified path is encrypted, you need to decrypt it in this method.
        return null;
    }

    public static void main(String[] args) {
        CustomClassLoader customClassLoader = new CustomClassLoader();
        try{ Class<? > clazz = Class.forName("One".true, customClassLoader);
            Object obj = clazz.newInstance();
            System.out.println(obj.getClass().getClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}` </pre>
Copy the code

About this

This class introduces

ClassLoader class, which is an abstract class from which all subsequent classloaders inherit (excluding the launcher ClassLoader)

Sun.misc.Launcher is a portal application for the Java virtual machine

How to obtain ClassLoader

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        try {
            / / 1.
            ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
            System.out.println(classLoader);
            / / 2.
            ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
            System.out.println(classLoader1);

            / / 3.
            ClassLoader classLoader2 = ClassLoader.getSystemClassLoader().getParent();
            System.out.println(classLoader2);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}` </pre>
Copy the code

Output result:

null
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d

Process finished with exit code 0` </pre>
Copy the code

Parent delegation mechanism

Principle of parental delegation mechanism

The Java virtual machine loads class files on demand, which means that it loads its class files into memory to generate class objects when the class is needed. In addition, when loading a class file, the Java VIRTUAL machine adopts the parental delegation mode, that is, the request to the parent class processing, which is a kind of task delegation mode

  1. If a classloader receives a classload request, it does not load the request itself. Instead, it delegates the request to the parent class’s loader. [Obtaining resources]

  2. If the parent class loader also has its parent class loader, then further delegate up, recursively, the request will eventually reach the top level of the start class loader;

  3. If the parent class loader can complete the task, it returns successfully. If the parent class loader cannot complete the task, the child loader will try to load itself. This is the parent delegate mode.

  4. The parent class loader allocates tasks layer by layer. If the subclass loader can load, it loads the class. If the system class loader cannot load the task, it throws an exception

Parent delegate mechanism code demonstration

For example 1

1. Create a java.lang.String class and write static code blocks

public class String {
    //
    static{
        System.out.println("I'm a static block of custom String class.");
    }
}` </pre>
Copy the code

2, in another program to load the String class, see if the String class is the JDK’s own String class, or our own String class

public class StringTest {

    public static void main(String[] args) {
        java.lang.String str = new java.lang.String();
        System.out.println("hello,atguigu.com");

        StringTest test = new StringTest();
        System.out.println(test.getClass().getClassLoader());
    }
}` </pre>
Copy the code

Output result:

hello,atguigu.com
sun.misc.Launcher$AppClassLoader@18b4aac2` </pre>
Copy the code

Instead of printing the contents of our static code block, we are still loading the JDK’s own String class.

I’m going to change that class

package java.lang;
public class String {
    //
    static{
        System.out.println("I'm a static block of custom String class.");
    }
    // Error: Main method not found in java.lang.String class
    public static void main(String[] args) {
        System.out.println("hello,String");
    }
}` </pre>
Copy the code

Output result:

hello,atguigu.com
sun.misc.Launcher$AppClassLoader@18b4aac2` </pre>
Copy the code

Instead of printing the contents of our static code block, we are still loading the JDK’s own String class.

I’m going to change that class

package java.lang;
public class String {
    //
    static{
        System.out.println("I'm a static block of custom String class.");
    }
    // Error: Main method not found in java.lang.String class
    public static void main(String[] args) {
        System.out.println("hello,String");
    }
}` </pre>
Copy the code

Bootstrap ClassLoader finds the JDK’s own String class. There is no main() method in that String class. So I reported the above error.

For example 2

package java.lang;

public class ShkStart {

    public static void main(String[] args) {
        System.out.println("hello!");
    }
}` </pre>
Copy the code

Output result:

java.lang.SecurityException: Prohibited package name: java.lang 	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662) 	at java.lang.ClassLoader.defineClass(ClassLoader.java:761) 	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) 	at java.net.URLClassLoader.access$100(URLClassLoader.java:73) 	at java.net.URLClassLoader$1.run(URLClassLoader.java:368) 	at java.net.URLClassLoader$1.run(URLClassLoader.java:362) 	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361) 	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) 	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495) Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" 
Process finished with exit code 1` </pre>
Copy the code

Package names such as java.lang are prohibited even if class names are not duplicated. It’s a protective mechanism

For example, 3

When we load jdbc.jar to implement the database connection

  1. Now we need to use the SPI interface in the program, and the SPI interface belongs to the Java core API in the Rt.jar package

  2. Then we use the parent delegate mechanism to boot the class loader to load the Rt. jar package in the rt.jar package, and the SPI package has some interfaces, interfaces we need concrete implementation classes

  3. Jar is the implementation class of SPI. For example, we load the SPI implementation class jdbc.jar.

  4. Classes in third-party JAR packages belong to the system class loader for loading

  5. The SPI core interface is loaded by the boot class loader, and the SPI implementation classes are loaded by the system class loader.

Advantages of parent delegation

From the example above, we can see that the parental mechanism can

  1. Avoid reloading classes
  2. Protect program security and prevent the core API from being tampered with
  • Custom classes: Custom java.lang.String is not loaded.
  • Custom class: java.lang.ShkStart (error: prevents creation of java.lang classes)

Sandbox security mechanism[Obtaining resources]

  1. When you customize a String class: Java.lang.string.jar: java.lang.string.jar: java.lang. This is because the String class is loaded in the rt.jar package.
  2. This ensures the protection of the Java core source code, which is called sandbox security.

other

How do I tell if two class objects are the same?

There are two requirements to indicate whether two class objects are the same class in the JVM:

  1. The full class name of the class must be consistent, including the package name

  2. The ClassLoader (ClassLoader instance object) that loads this class must be the same

  3. In other words, in the JVM, even if these two class objects come from the same class file and are loaded by the same virtual machine, they are not equal as long as the ClassLoader instance objects that loaded them are different

A reference to the class loader

  1. The JVM must know whether a type is loaded by the boot loader or by the user class loader

  2. If a type is loaded by a user class loader, the JVM stores a reference to that class loader in the method area as part of the type information

  3. When parsing a reference from one type to another, the JVM needs to ensure that the classloaders for both types are the same (more on that later).

In the end, I wish you all success as soon as possible, get satisfactory offer, fast promotion and salary increase, and walk on the peak of life.

If you can, please give me a three support me?????? [Obtain information]