JVM learning (four) class loading

problem

  • Class loading mechanism, there are several steps for loading a class into a virtual machine. Which order of these steps is fixed and which is not fixed, why not

Answer: 1. Load 2. Verify 3. Prepare 4. Static analysis (not fixed) 5. Initialize 6. Use 7. Dynamic analysis (not fixed) 8. Unload the reference connection: https://www.jianshu.com/ p / 2a3cdc027c2c

Overview

  • Java class loading mainly gives three steps
  1. load
  2. Link (also divided into three small steps of verification, preparation and analysis)
  3. initialization
  • There is an important principle in loading: the parent delegation model.

load

The first step in the class loading process is to complete the following three things:

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

java class identifier

At runtime, the identity of a class is determined not only by its name, but also by its binary name and the loader that defines it. Each class or interface belongs to a separate run-time package. The definition of classes and interfaces in this run-time package is determined by the package name and the loader that defines it.

We know that there are two types of Java language types: reference types and primitive types. The basic types are pre-defined by the Java virtual machine. The reference types are divided into four types by Java: classes, interfaces, Array class and generic parameters. Since generic parameters will be erased during compilation, there are actually only three types of java virtual machines: interfaces, classes, and arrays.

Interfaces and classes are loaded by the class loader, while array types are not loaded by the class loader, but by the jvm.

Overview of the loading process

The following description comes from: https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html

When introducing the proxy mode of the class loader earlier, it was mentioned that the class loader will first proxy to other class loader to try to load a certain class. This means that the class loader that actually completes the class loading work and the class loader that initiated the loading process may not be the same. The loading of the class is accomplished by calling defineClass; the loading process of the startup class is achieved by calling loadClass. The former is called a class defining loader, and the latter is called the initial loader. When the Java virtual machine determines whether two classes are the same, the class definition loader is used. In other words, it does not matter which class loader initiates the class loading process. What matters is that the class loader is finally defined. The relationship between the two class loaders is that the definition loader of a class is the initial loader of other classes it references. If the class com.example.Outer refers to the class com.example.Inner, the definition loader of the class com.example.Outer is responsible for starting the loading process of the class com.example.Inner.

Parent commissioned loading

1297993-20200416102038427-116171272.jpg

It should be noted that the above class loaders are not inherited (extended) relationships but combined holding relationships. When a subclass loads a class, it will first be handed over to the parent class to load. If the parent class loads After that, the parent class returns the loaded class. If the parent class cannot be loaded, then the child class loader will load it.

public abstract class ClassLoader {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    // 持有的父类类加载器
    private final ClassLoader parent;


    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                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.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

}


Annotation of loadClass method.

Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:
1. Invoke findLoadedClass(String) to check if the class has already been loaded.
2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
3. Invoke the findClass(String) method to find the class.

If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object.

Subclasses of ClassLoader are encouraged to override findClass(String), rather than this method.
Unless overridden, this method synchronizes on the result of getClassLoadingLock method during the entire class loading process.

among them

  • Bootstrap ClassLoader: used to load the java core library. It is mainly the jre / lib directory (from the environment variable sun.boot.class.path). It is written in C ++ and is part of the virtual machine itself, and it cannot be referenced in java code.
  • Extensions ClassLoader: used to load java extension library.
  • Application ClassLoader: used to load the library class in the running java application, you can get it through ClassLoader.getSystemClassLoader (). For example, the library class in the pom.xml file.

The loading process of the above code is the proxy mode

The proxy mode is to ensure the type safety of the Java core library. All Java applications must at least refer to the java.lang.Object class, which means that at runtime, the java.lang.Object class needs to be loaded into the Java virtual machine. If this loading process is completed by the Java application's own class loader, there may be multiple versions of the java.lang.Object class, and these classes are incompatible. Through the proxy mode, the loading of the classes of the Java core library is completed by the boot class loader, which ensures that the Java applications use the same version of the class of the Java core library and are compatible with each other.

The following is a schematic diagram of the parent commissioned loading, the picture is from reference materials:

1297993-20200416135145267-264158634.jpg

Why use the parent delegation model?

One thing is for safety. If the basic class java.lang.Object is loaded in the parent class, and a development loads the same Object from elsewhere, it will fail because the parent class has already been loaded, which also guarantees The security of core class loading in java.

Destruction of the parent delegation model

First of all, we need to know what is considered to be broken. Before the parent delegation mode is loaded, it must be handed over to the parent for loading. The meaning of destruction is: the subclass loader is no longer loaded from the parent class loader, but loaded by itself.

Examples of destruction of parental delegation

Example source: https://juejin.im/post/5a59f2296fb9a01ca871eb8c

What if the base class calls the user's code? A typical example is the JNDI service. JNDI is now a Java standard service. Its code is loaded by the startup class loader (rt.jar that was put in in JDK1.3), but it needs to be called by an independent vendor. And deploy the code of the JNDI interface provider (SPI, Service Provider Interface) under the ClassPath of the application, but it is impossible for the startup class loader to "know" these codes. Because these classes are not in rt.jar, but the startup class loader needs to be loaded. How to do it?

The java team introduced a thread context class loader.

Thread context class loader

The thread context class loader (context class loader) was introduced from JDK 1.2. The methods getContextClassLoader () and setContextClassLoader (ClassLoader cl) in class java.lang.Thread are used to get and set the context class loader of the thread. If not set by the setContextClassLoader (ClassLoader cl) method, the thread will inherit the context class loader of its parent thread. The context class loader of the initial thread that the Java application runs is the system class loader (Application ClassLoader). Code running in a thread can load classes and resources through this class loader.

Get the current thread context loader

Thread.currentThread().getContextClassLoader();
ClassLoader.getSystemClassLoader();

With the thread context loader, the JNDI service uses this thread context loader to load the required SPI code, that is, the parent class loader requests the child class loader to complete the class loading action. This behavior is actually to get through the parents The hierarchy of the delegation model to reverse the use of class loaders actually violates the general principles of the parent delegation model

Use a custom class loader

Create a java program under a path as follows:

public class Test{
    private int age; 
    private String name;
    public Test(){
        this.age = 15;
        this.name ="Aaaa";
    }
}

Then javac Test.javagenerate the class file.

public class MyClassLoader extends ClassLoader {


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File file = getClassFile(name);
        try {
            byte[] bytes = getClassBytes(file);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    private File getClassFile(String name) {

        return new File("C:\\Users\\Administrator\\Desktop\\Test.class");
    }

    private static byte[] getClassBytes(File file) throws Exception {
        FileInputStream fis = new FileInputStream(file);
        ByteArrayOutputStream aos = new ByteArrayOutputStream(fis.available());
        byte[] bytes = new byte[fis.available()];    //使用fis.avaliable()方法确保整个字节数组没有多余数据
        fis.read(bytes);
        aos.write(bytes);
        fis.close();
        return aos.toByteArray();
    }

    public static void main(String[] args) throws Exception {
        MyClassLoader ct = new MyClassLoader();
        Class c = Class.forName("Test", true, ct);
        System.out.println(c.getClassLoader());
    }
}

Run to see the result.

supplement

  • The parent delegation model is also destroyed in tomcat
  • For hot deployment recommendations, see "In-depth Understanding of the Java Virtual Machine." Online blogs are written in a mess.

References

  • https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3.2 (official document)
  • https://www.cnblogs.com/aspirant/p/8991830.html (see)
  • https://www.cnblogs.com/huxuhong/p/11856786.html (see)
  • https://www.jianshu.com/p/9df9d318e838

Guess you like

Origin www.cnblogs.com/Benjious/p/12712816.html