“This is the first day of my participation in the Gwen Challenge in November. See details of the event: The last Gwen Challenge in 2021”.

This article is mainly about the JAVA virtual machine class loading mechanism, a detailed explanation of the map, from the cocoon.

Class loading details

Java class loading overview

When the program runs, the class file class loads the bytecode into the constant pool through the ClassLoader, and verifies, prepares, parses, and initializes it.

The VM black box performs verification, preparation, parsing, and initialization. But loading is not a virtual machine thing.

What is class loading

Class loading is when the class loader fetches the binary byte stream of a class through its fully qualified name to the JAVA virtual machine. The diagram below:

As can be seen from the graph:

  • The class-loading operation of “getting the binary stream of a class by its fully qualified name” is implemented outside the virtual machine
  • Class loading leaves it up to the programmer to decide how to get the required classes.
  • The code module that helps the programmer implement this loading action is called a ClassLoader.

Loading a Classloader actually does three things:

  1. Gets the binary byte stream that defines a class by its fully qualified name. The binary stream of bytecode can also be computed at run time. Dynamic Proxy technology Proxy uses ProxyGenerator to generate binary stream of $Proxy for a specific interface.
  2. Transform the static storage structure represented by this byte stream into the runtime data structure of the method area
  3. Generate an in-memory Class object representing the Class as an access point to the Class’s various data structures in the method area.

Class and classloader relationship

Classes loaded by different class loaders are placed inside the virtual machine. If two different classloaders both load the same class, but they are not considered the same class. Why is that?

As shown above, Classloader1 and classloader2 both load the A.B.A class file.

However, there are two instances of Class named A.B.A in the virtual machine, as shown in the following figure

The logic for findClass and defineClass is defined in the ClassLoader(programmers can customize their own classloaders) and generates two different Class objects.

The Class of the world

Let’s take a look at the Class object

  • Object (which represents everything), Class (which represents bytecode compiled by Java files)
  • Object class: Has no members
  • Class member :classloader name…

So when the JVM loads a class, it loads the Object class first. Which class loader loads the Object class? The answer to this question is given below.

There is an obvious bug in the diagram above to demonstrate ClassLoader loading classes, which will be corrected below.

A little more detail about class loading – arrays

  • The loading stage of non-array classes can be completed by using the bootstrap class loader provided by the system, or by using user-defined class loaders. Developers can define their own class loaders to control the way of obtaining byte streams.

  • The array class is special: the array class itself is not created by the class loader, it is created directly by the Java virtual machine, which creates an array primarily by considering how much memory to allocate.

  • The element class of the data type is the reference type B,

B is the ClassLoader C1 that is created by the ClassLoader C1 load but loads B on the array class object tag. Finally, the virtual machine creates Class objects directly (Vector B[10],C1)

  • Non-reference types, such as int,

The loading of element types is associated with the bootstrap class loader. Finally, the virtual machine creates the Class object directly (vector int[10], bootstrating the classloader)

The timing of class loading

When does a class load?

The JVM does not enforce when a class needs to be initialized, but it does enforce when a class needs to be initialized (which means that the class needs to be loaded), summarized in five points:

  • When a VM starts, the user needs to specify a main class (containing the main method) to execute. The VM initializes the main class first

As shown in the figure above, when the VM starts, Main is the Main class of execution. The vm initializes Main first

  • When initializing a class, if the parent class has not already been initialized, the parent class must be initialized first.

    The Object class is the parent of all classes, so in the virtual machine, the Object class must be the first to be initialized. You can see that in the picture above.

  • Four bytecode instructions, new getstatic putstatic Invokestatic, trigger initialization of the class if it has not already been initialized.

  • Special case: Final fields are special and are put into the calling class’s constant pool at compile time, so referring directly to the class’s static constant fields does not cause class initialization.

If the following two need, how to explain it.

  • Reflect’s package makes a reflection call to a class that has not already been initialized.
  • REF_getStatic (REF_getStatic, REF_getStatic, REF_getStatic, REF_getStatic);

Learn about common class loaders

  • Bootstrap ClassLoader to load the JDK core lib
  • Extension ClassLoader Extension ClassLoader,JDK Extension mechanism package
  • A class loader programmed by the programmer to load libraries specified on the user’s Classpath.

If the APP does not define its own Classloader, the Application Classloader is generally the default Classloader.

  • Understand that classpath is the Application Classloader scope (each JVM should have its own classpath, such as the directory where the current program is running, local Maven repository, etc.)

  • As you can see here, there are three scopes: JVM Lib and extension and CLASspath.

Which Classloader loads the Object class?

How things happen:

  • When the virtual machine starts, Main is the Main class for execution. The virtual machine initializes Main first

  • According to the initialization principle, the parent Object class is initialized first

  • The Application Classloader delegates the model to the Extension Classloader via parent

  • Delegate the Extension Classloader to the BootStrap Classloaderloader

  • BootStrap Classloaderloader loads the Object class

  • The Applciation Classloader assigns the Main class to the parent, but the parent does not find it, and then finds the Main object in the classpath

The parental delegation model is covered in more detail

ClassLoader parent inheritance mechanism

Each Classloader must define its own parent (if null, the default is Bootstrap Classloader). The parent delegate model means that each class is assigned to a loadClass by the parent. If the parent finds it, I don’t need to find it. Or I’ll deal with it. The current ClassLoader in the JAVA world is basically as follows:

If you don’t use the parent delegate model, you can define your own java.lang.Object. If you don’t use the parent delegate model, you can define a java.lang. And use the default Application Classloader to load the Object instead, and the world is out of order.

Implementation code for parental delegation:

protectedClass<? > loadClass(String name,boolean resolve) 
{
    synchronized (getClassLoadingLock(name)) {
        // If this class already exists in the virtual machine, it will be returned directlyClass<? > c = findLoadedClass(name);if (c == null) {
            long t0 = System.nanoTime();
            try {
          // Delegate to parent
                if(parent ! =null) {
                    c = parent.loadClass(name, false);
                } else{ c = findBootstrapClassOrNull(name); }}/ /...
            if (c == null) {
                // Try to find the class
                longt1 = System.nanoTime(); c = findClass(name); . }...returnc; }}Copy the code

LoadClass: if this class already exists in the virtual machine, return it. The world of Class is the world of Class.

It can also be seen that the parent delegate model gives different permissions for several scopes of a class:

C2 loads class A (no more because it was found) with C2’s implementation strictly adhering to the parental delegation model.

Because of this loading mechanism, when you load by a fully qualified name,

  • Classes in the JDK lib Domain have the highest priority,
  • JDK lib Ext Domain classes have second priority,
  • Classes in the Classpath Domain have third priority
  • Classes generated by other domains (network, code generation) need to be loaded by user-defined ClassLoader.

Classloader isolation and visibility issues

It’s the same diagram as above, which cannot exist in the parent delegate scenario.

But if you don’t follow this mechanism, it can still exist. But what if there are two class objects in the JVM with the same name?

Here’s a little experiment

package com.ywz;
public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ClassLoader myloader=new ClassLoader() {
            @Override
            publicClass<? > loadClass(String name)throws ClassNotFoundException {
                try {
                    String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    // Ready to load the class: classloadertest.class
                    InputStream stream = getClass().getResourceAsStream(filename);
                    if (stream == null) {
                        return super.loadClass(name);
                    } else {
                        byte[] bytes = new byte[stream.available()];
                        stream.read(bytes);
                        return defineClass(name, bytes, 0, bytes.length); }}catch (IOException e) {
                    throw newClassNotFoundException(name); }}}; Class loadClass = myloader.loadClass("com.ywz.ClassLoaderTest");
        // Class objects are equal
        System.out.println("class equal ? " + loadClass.equals(com.ywz.ClassLoaderTest.class));
        // Whether the names of class objects are equal
        System.out.println("class name equal ? " + loadClass.getName().equals(com.ywz.ClassLoaderTest.class.getName()));
        // Outputs the ClassLoader for the class object
        System.out.println("the classLoader = " + loadClass.getClassLoader());
        System.out.println("default classLoader = "+ com.ywz.ClassLoaderTest.class.getClassLoader()); }}Copy the code

Results obtained

class equal ? false
class name equal ? true
the classLoader = com.ywz.ClassLoaderTest$1@d716361
default classLoader = sun.misc.Launcher$AppClassLoader@18b4aac2
Copy the code

That is to say, Classloader has visibility issues. Classloader can only see its loaded classes, but it can’t see other classes. So a class object has visibility problems.

  • App ClassLoader loads the ClassLoaderTest class

  • MyClassLoader loading ClassLoaderTest class (break the delegate model, load)

So now there are two A.B.classLoaderTest Class objects in the system, but the default Class loader is AppClassLoader. Use com.ywz.ClassLoaderTest to get the Class object loaded by AppClassLoader.

Default class loader

As you can see from the above

  • The default Classloader for JVM programs to run is the APP Classloader
  • When aClassLoaderLoading a class unless another class is explicitly usedClassLoader, the class depends on and references the class also by thisClassLoaderLoad. To put it simply, what class loader is used for entry to the program, and what classes follownewThe output object also uses the class loader.

An open class loading mechanism

Flexible class loading

The JVM opened up class loaders for the convenience of programmers, an innovation of the Java language.

We can define our own classloader to do some useful things like

  • Load the encrypted bytecode
  • Hot loading: The development phase can send bytecodes that the phase listens for changes, reload these classes using custom class loaders, and restart main using reflection.

Break the parent inheritance model

The figure above defines a myClassloader that breaks parental inheritance, but that doesn’t matter in my little world.

In fact, the JVM world has some examples of large-scale destruction of the parent inheritance model, such as JNDI.

Parental delegation is a good solution to the problem of unifying the base classes that solve the various classloaders, but what if the base class wants to call user code?

A typical example is JNDI, whose code is loaded by the startup class loader (put into rt.jar in JDK 1.3), but JNDI is for centralized management and lookup of resources, It calls the code of the JNDI interface provider implemented by an independent vendor and deployed in the application’s ClassPath (which is what the APP ClassLoader is supposed to do). Bootstrap ClassLoader cannot load code from ClassPath.

As you can see, JNDI wants to load code using classpath, but there are two problems:

  • This destroys parental inheritance
  • Bootstrap ClassLoader cannot load code in classpath

To solve the second problem, the JVM introduces the Thread Context ClassLoader.

The thread inherits the parent class’s Context ClassLoader, which uses App ClassLoader by default if it is not set at the global level of the application.

In this case, the problem can be solved as follows:

As you can see, JNDI uses contextClassLoader to load the code under classpath.

Introducing such a design completely breaks the parent inheritance model at the Thread level.

But it solves the problem.

reference

  • An in-depth understanding of the JAVA Virtual Machine (Second edition)