The Java compiler compiles the “.java “code file into the”.class “bytecode file, and the classloader is responsible for loading the bytecode file’s classes into the JVM when needed. What happens during loading? Class loaders. And how different class loaders make sure they don’t load the same class twice. These questions will be addressed below

The bytecode

Before we talk about class loading mechanisms, we need to take a look at Java bytecode, as it relates to the process of class loading.

Java was born with the slogan “Write Once, Run AnyWhere.” To that end, Sun released a number of JVMS that ran on different platforms — responsible for loading and executing Java compiled bytecode.

Let’s take a look at what bytecode looks like:

cafe babe 0000 0034 014c 0a00 3e00 950a
0096 0097 0900 3d00 980b 0099 009a 0900
3d00 9b0a 009c 009d 0a00 9e00 9f0a 00a0
00a1 0b00 9900 a207 00a3 0a00 0a00 a409
003d 00a5 0a00 a600 a70a 00a8 00a9 0b00
9900 aa08 00ab 0b00 ac00 ad07 00ae 0a00
9c00 af08 00b0 0a00 b100 b20a 00b3 00b4
0a00 1200 b50a 0012 00b6 0a00 b100 b70a
00b8 00b9 0a00 3d00 ba0a 00b1 00a9 0a00
b100 bb09 003d 00bc 0a00 1200 bd0a 0012
Copy the code

Cafe Babe in this bytecode is called a “magic number” and is the JVM’s signature for recognizing.class files.

The JVM is responsible for loading such files and interpreting them into a language that the computer can understand.

When does a JVM load a class?

First of all, when do you load a class?

Well, the answer is pretty simple when you use this class in your code.

There has to be a head, which class is loaded at the beginning? Which brings us to the bootstrap class

public static void main(String[] args){
    Manager manager = new Manager();
}
Copy the code

Is the origin of everything, is also a Java rule.

To recap: The main class in your code that contains the “main()” method must be loaded into memory after the JVM process starts to execute the code in your “main()” method.

Then if you use another class, such as “Manager”, the corresponding class will be loaded into memory from the corresponding “.class “bytecode file.

The process of class loading

From load to use, a class typically goes through the following process:

Load -> Verify -> Prepare -> Parse -> Initialize -> Use -> Uninstall

1. Loading

The main purpose of the JVM at this stage is to load the bytecode from various data sources (be it a class file, a JAR package, or even a network) into a binary stream into memory and generate a Java.lang.class object that represents that class.

2. Verification

The JVM verifies the binary byte stream at this stage, and only those that meet the JVM bytecode specification will be executed correctly by the JVM. This stage is an important barrier to JVM security, and here are some of the main checks.

  • Ensure that the binary byte stream format is as expected (e.gcafe beneAt the beginning).
  • Whether all methods comply with the qualification of the access control keyword.
  • Whether the method call has the correct number and type of arguments.
  • Make sure variables are properly initialized before use.
  • Check that the variable is assigned a value of the appropriate type.

3. Preparation

At this stage, the JVM allocates memory for class variables (also known as static variables, modified by the static keyword) and initializes them (default initial values for the corresponding data types, such as 0, 0L, NULL, false, and so on).

Here’s an example:

public String chenmo = "Silence";
public static String wanger = "Two";
public static final String cmower = "Silent King II.";
Copy the code

Chenmo will not be allocated memory, wanger will; But the initial value for Wanger is not “King two” but null.

Note that static final variables are called constants, as opposed to class variables. Constants do not change once assigned, so cMOwer’s value in the preparation phase is “Silent King two” instead of null.

4. Resolution

This phase converts symbolic references in the constant pool to direct references.

First of all, we need to explain that at compile time, Java classes do not know the actual address of the referenced class, so they have to use symbolic references instead. For example, the com.wanger class references the com.chenmo class. At compile time, the Wanger class does not know the actual memory address of the Chenmo class, so it can only use the symbol com.chenmo. Direct references find the actual memory address of the reference by parsing symbolic references.

A more detailed explanation can be found in this article

5. Initialization

The preparation phase is just to create a memory space for the class variable and then give it an initial value, so the assignment phase is executed in the initialization phase, and the static code block is also executed in the initialization phase.

public class ClassLoaderDemo {
	public static int i = 5;
	static {
		test();
	}
	public static void test(a) {
		System.out.println("Initialization in progress")}}Copy the code

That is, in the code above, I is assigned a value of 5, and the static code block will be executed at this stage.

In addition, there is an important rule that if a class is initialized and its parent is not initialized, it must initialize its parent first.

For example,

Given the following code: Evaluate counter1 and counter2

public class Classloader {
	private static Classloader classloader = new Classloader(); / / 1
	public static int counter1; / / 2
	public static int counter2 = 0; / / 3
	public Classloader(a) {
		counter1++; 
		counter2++;
	}
	public static Classloader getClassloader(a) {
		returnclassloader; }}// Run the main class
public class TestClassloader {
	public static void main(String[] args) { Classloader classloader = Classloader.getClassloader(); System.out.println(classloader.counter1); System.out.println(classloader.counter2); }}Copy the code

The answer is

counter1 == 1
counter2 == 0
Copy the code

Step by step, in the preparation phase, assign the default initial value to the static variable, i.e

classloader = null;

counter1 = 0;

counter2 = 0;

In the next initialization phase, static variables are initialized from the top down, so new CLassloader() is executed first; The constructor is then executed, so the following counter1 = 1, counter2 = 1;

Initialization continues, but no assignment is made to counter1 during the initialization phase, so the final result is counter1 = 1, counter2 = 0;

You can also try placing statement 1 after statement 3 and output counter1 = 1, counter2 = 1;

Class loader

Java class loaders have the following types:

  • Bootstrap ClassLoader

<$JAVA_HOME>/ jre/lib/rt.jar (class or Xbootclassoath) Implemented by **C++**, not ClassLoader subclass.

  • Extension ClassLoader

It is responsible for loading some JAR packages of extension functions in the Java platform, including <$JAVA_HOME>/jre/lib/ext/*.jar or jar packages in the specified directory -djava.ext.dirs.

  • Application ClassLoader (App ClassLoader)

Is responsible for loading jars specified in classpath and classes and JARS specified in djava.class. path. It is the default class loader for Java applications.

  • Custom class loaders

Classes can be customized by subclasses of java.lang.ClassLoader, which are customized by application programs according to their own needs. For example, Tomcat and JBoss implement ClassLoader by themselves according to j2ee specifications.


During vm startup, BootstrapClassLoader is initialized, and ExtClassLoader and AppClassLoader are loaded in the Launcher class. The parent of AppClassLoader is set to ExtClassLoader and the thread context classloader is set.

Launcher is the class in the JRE that launches the program entry main(). Let’s look at the code for the Launcher

public Launcher(a) {
        Launcher.ExtClassLoader var1;
        try {
            // Load the extension class loader
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            // Load the application class loader and set parent to extClassLoader
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        // Set the default thread context class loader to AppClassLoader
        Thread.currentThread().setContextClassLoader(this.loader);
        // Delete irrelevant code here...
        }
Copy the code

The main reason why ExtClassLoader does not set parent is because BootstrapClassLoader is implemented in C++, so there is no Java class, so if you try to implement it

 public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = Test.class.getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());
        System.out.println(classLoader.getParent().getParent());
    }
Copy the code

It will print something like this

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@5a61f5df
null
Copy the code

Parent delegation mechanism

A classloader first passes the classloading request to the parent classloader and only tries to load it if the parent classloader is unable to complete the classloading request.

purpose

First, the virtual machine determines that two classes are a class only if they have the same class name and are loaded by the same loader. In order to solve the developer custom class name and the official class may load conflicts. Therefore, a hierarchical load relationship with specific priorities is used.

For example, the java.lang.Object class, which resides in rt.jar, is delegated to the Bootstrap ClassLoader at the top of the model by any ClassLoader. Therefore, the Object class is the same class in each ClassLoader environment of the program. On the other hand, if a user writes a java.lang.Object Class of the same name and places it in the class-path, the system will have multiple Object classes and the program will be confused. So, if a developer tries to write a Java class with the same name as one in the Rt.jar library, it will compile fine, but it will never be loaded and run.


Custom class loaders

To implement a ClassLoader, simply inherit the ClassLoader class and override the findClass (String Name) method to create a ClassLoader with a parent delegate model.

ClassLoader

The following is a snippet of the abstract java.lang.classloader class, where the loadClass() method works as follows: first check to see if the class has been loaded, and if not, let the parent ClassLoader load it. Throw a ClassNotFoundException when the parent class loader fails to load, then try to load it yourself.

Pseudo code:

public abstract class ClassLoader {
    // The parent class loader for delegation
    private final ClassLoader parent;

    publicClass<? > loadClass(String name)throws ClassNotFoundException {
        return loadClass(name, false);
    }

    protectedClass<? > loadClass(String name,boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loadedClass<? > c = findLoadedClass(name);if (c == null) {
                try {
                    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
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.c = findClass(name); }}if (resolve) {
                resolveClass(c);
            }
            returnc; }}protectedClass<? > findClass(String name)throws ClassNotFoundException {
        throw newClassNotFoundException(name); }}Copy the code

At this point, some of you might wonder, right? Why not inherit ExtClassLoader or AppClassLoader?

Because AppClassLoader and ExtClassLoader are static internal classes of the Launcher, they both have package access path permissions.


Below is the code that implements the custom ClassLoader

public class MyClassLoader extends ClassLoader {
    /** * Overridden the superclass method, returns a Class object * This method should be overridden by Class loader implementations */
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        Class clazz = null;
        String classFilename = name + ".class";
        File classFile = new File(classFilename);
        if (classFile.exists()) {
            try (FileChannel fileChannel = new FileInputStream(classFile)
                    .getChannel();) {
                MappedByteBuffer mappedByteBuffer = fileChannel
                        .map(MapMode.READ_ONLY, 0, fileChannel.size());
                byte[] b = mappedByteBuffer.array();
                clazz = defineClass(name, b, 0, b.length);
            } catch(IOException e) { e.printStackTrace(); }}if (clazz == null) {
            throw new ClassNotFoundException(name);
        }
        return clazz;
    }

    public static void main(String[] args) throws Exception{
        MyClassLoader myClassLoader = new MyClassLoader();
        Class clazz = myClassLoader.loadClass(args[0]);
        Method sayHello = clazz.getMethod("sayHello");
        sayHello.invoke(null.null); }}Copy the code

reference

This article refers to zhihu users please call me program ape adult good afraid of class loader article