Class loading

For any class, its uniqueness within the Java Virtual machine is determined by the classloader that loads it and by the class itself.

Loading a class involves reading binary data from a class’s.class file into memory, placing it in the method area of the runtime data area, and then creating a java.lang. class object in the heap that encapsulates the class’s data structure in the method area.

The end product of Class loading is the Class object in the heap, which encapsulates the Class’s data structure in the method area and provides the Java programmer with an interface to access the data structure in the method area.

Instead of waiting for a class to be “first actively used” before loading it, the JVM specification allows the classloader to preload a class in anticipation of it being used, and if it encounters a missing.class file or an error during preloading, The class loader must not report a LinkageError until the program first actively uses the class. If the class is never actively used by the program, the class loader will not report an error

How to load a. Class file

  • Load directly from the local system
  • Download the. Class file from the network
  • Load. Class files from zip, JAR, etc archives
  • Extract. Class files from a proprietary database
  • Dynamically compile Java source files into.class files

View the class file

Idea using the jclasslib plugin to view

Vim -b xxx.class opens files in binary :%! XXD Displays hexdump -c xxx.class hexadecimal displays Javap -c -v xxx.class disassemblyCopy the code

The process of class loading

The process of class loading includes five stages: loading, verification, preparation, parsing and initialization. Of the five phases, the loading, validation, preparation, and initialization phases occur in a certain order, while the parsing phase does not, and in some cases can begin after the initialization phase, in order to support runtime binding (also known as dynamic binding or late binding) in the Java language. Also note that the phases here start in sequence, not proceed or complete sequentially, as these phases are often intermixed with each other, often invoking or activating one phase while the other is executing.

1. The load

Obtain the binary byte stream defined by the fully qualified name of a Class, convert the static storage structure represented by the byte stream into the runtime data structure of the method area, and generate a Java.lang. Class object representing the Class in the Java heap as an access point to the data in the method area.

Compared to other phases of class loading, the loading phase (specifically, the action of the loading phase to retrieve the binary byte stream of the class) is the most controllable, because developers can either use the system-provided class loader to complete the loading, or they can customize their own.

After the loading phase is complete, the external binary byte stream is stored in the method area in the format required by the virtual machine, and an object of java.lang.Class is also created in the Java heap

2. Verify

  • File format verification: verify whether the byte stream conforms to the Class file format specification; For example, whether the value starts with 0xCAFEBABE, whether the major and minor versions are within the processing range of the current VM, and whether the constants in the constant pool have unsupported types.
  • Metadata verification: Semantic analysis of the information described by bytecode (note: compared with the semantic analysis in the compilation phase of JavAC) to ensure that the information described conforms to the requirements of the Java language specification; For example, does this class have a parent other than java.lang.object?
  • Bytecode validation: Determine that program semantics are legitimate and logical through data flow and control flow analysis.
  • Symbol reference validation: Ensures that the parse action is performed correctly.

3. Prepare

The phase that allocates memory for class variables and sets their initial values, which will be allocated in the method area

Only class variables (static) are allocated, not instance variables, which are allocated in the Java heap along with the object at instantiation.

The initial value set here is usually the default zero value of the data type (such as 0, 0L, NULL, false, etc.), not the value explicitly assigned in Java code, unless modified by both static and final.

4. The parsing

The virtual machine replaces symbolic references in the constant pool with direct references. The resolution action is mainly for class or interface, field, class method, interface method, method type, method handle, and call point qualifier.

A symbolic reference is a set of symbols that describe a target, which can be any literal. A direct reference is a pointer to a target directly, a relative offset, or a handle to the target indirectly.

5. The initialization

The < clinit > () method is a compiler that collects assignment statements, static code blocks, and class initialization methods in the order in which they appear.

JVM initialization steps

  1. If the class has not already been loaded and connected, the program loads and connects the class first
  2. If the class’s immediate parent has not already been initialized, its immediate parent is initialized first
  3. If there are initializers in a class, the system executes those initializers in turn

Class initialization timing

  1. Note that the Newarray directive triggers only the initialization of the array type itself, not its associated type. For example, New String[] will only trigger the initialization of the String[] class directly, that is, the initialization of the ljava.lang.String class. The most common Java code scenarios that generate these four instructions are: When an object is instantiated with the new keyword; when a static field of a class (modified by final, except when the compiler has put the result into the constant pool) is read or set; when a static method of a class is called.

  2. When a reflection call is made to a class using the java.lang.Reflect package’s methods, initialization needs to be triggered if the class has not already been initialized.

  3. When initializing a class, if the parent class has not been initialized, the initialization of the parent class must be triggered first.

  4. When the virtual machine starts, the user needs to specify a primary class (the one containing the main() method) to execute, and the virtual machine initializes this primary class first.

  5. When using jdk1.7 dynamic language support, if a Java lang. Invoke. The final analytical results REF_getstatic MethodHandle instance, REF_putstatic, REF_invokeStatic method handles, And the class corresponding to the method handle is not initialized, so it needs to be initialized first.

Class loading mode

Classes can be loaded in three ways: 1. They are initialized by the JVM when the application is started on the command line. 2

Class.forname () and classLoader.loadClass ()

  • Class.forname () : In addition to loading the Class’s.class file into the JVM, the Class is interpreted, executing the static block of the Class;
  • Classloader.loadclass () : does only one thing: loads a. Class file into the JVM. Static blocks are executed only at newInstance.
  • Class. ForName (name, initialize, loader) functions with parameters can also control whether static blocks are loaded. The constructor is called only when the newInstance() method is called to create an object of the class.
package com.neo.classloader;
public class loaderTest { 
    public static void main(String[] args) throws ClassNotFoundException { 
            ClassLoader loader = HelloWorld.class.getClassLoader(); 
            System.out.println(loader); 
            // Use classloader.loadClass () to load classes without performing initialization blocks
            loader.loadClass("Test2"); 
            // Use class.forname () to load the Class, which executes the initialization block by default
            Class.forName("Test2"); 
            // Use class.forname () to load the Class and specify a ClassLoader. Static blocks are not executed when initialized
            Class.forName("Test2".false, loader); }}Copy the code

Class loader

Class loader structure

Classloaders in Java can be roughly divided into two classes, one provided by the system and the other written by Java application developers. The system provides the following three class loaders: • bootstrap class loader: it is used to load Java core libraries. It is implemented in native code (C++) and does not inherit from java.lang.ClassLoader. • Extensions Class Loader: It is used to load Java extension libraries. The Implementation of the Java Virtual machine provides an extension library directory. The class loader finds and loads Java classes in this directory. • System Class Loader: It loads Java classes based on the Java application’s CLASSPATH. In general, Java application classes are loaded by it. Through this. GetSystemClassLoader () to get it.

Parental delegation model

  1. When an AppClassLoader loads a class, it first does not attempt to load the class itself. Instead, it delegates the classloading request to the parent class loader, ExtClassLoader.
  2. When ExtClassLoader loads a class, it doesn’t try to load the class itself in the first place. Instead, it delegates the class loading request to BootStrapClassLoader.
  3. If the BootStrapClassLoader fails to load (for example, the class cannot be found in $JAVA_HOME/jre/lib), the ExtClassLoader will be used to try to load the class.
  4. If the ExtClassLoader also fails to load, AppClassLoader is used to load it. If the AppClassLoader also fails to load, ClassNotFoundException is reported.
protectedClass<? > loadClass(String name,boolean resolve)
    throws ClassNotFoundException{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loadedClass<? > c = findLoadedClass(name);// Check to see if it is already loaded
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if(parent ! =null) {// If it is not loaded, pass it to the parent loader first
                    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 the parent loader is not loaded, it will be loaded itself
                // 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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {
            resolveClass(c);
        }
        returnc; }}Copy the code

Custom class loaders

In addition to the system-provided classloaders, developers can implement their own classloaders by inheriting java.lang.ClassLoader classes to meet special needs. If you need to customize a ClassLoader, you can inherit the ClassLoader class and find the class method again. If the upper layer does not, it will try to load it itself.

public class MyClassLoader extends ClassLoader {

    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        File file = new File("D:/aliyun-test/target/classes/com/aliyun/domain/BaseEntity.class");
        try {
            byte[] bytes = getClassBytes(file);
            // The binary stream is java.lang.class
            return this.defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    private byte[] getClassBytes(File file) throws Exception {
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);

        while (true) {
            int i = fc.read(by);
            if (i == 0 || i == -1)
                break;
            by.flip();
            wbc.write(by);
            by.clear();
        }
        fis.close();
        return baos.toByteArray();
    }

    public static void main(String[] args) throws Exception {
        MyClassLoader mcl = newMyClassLoader(); Class<? > clazz = Class.forName("com.aliyun.domain.BaseEntity".true, mcl);
        BaseEntity baseEntity = (BaseEntity) clazz.newInstance();

        System.out.println(baseEntity);
        System.out.println(baseEntity.getClass().getClassLoader());
    }
Copy the code

Thread context loader

The thread-context class loader looks at the loading of classes from the perspective of threads. Binding a class loader to each thread can liberate the loading of classes from the simple parent loading model, and then realize specific loading requirements.

public Launcher(a) {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }
    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }
    // Set the AppClassLoader as the context loader for the current thread
    Thread.currentThread().setContextClassLoader(this.loader);
    / /...
}
Copy the code