Java class loader

Basic concepts of class loaders

The Class loader is used to load Java classes into a Java VIRTUAL machine. Generally, Java virtual machines use Java classes in the following ways: Java source programs (.java files) are converted into Java bytecode (.class files) after being compiled by the Java compiler. The classloader is responsible for reading the Java bytecode and converting it into an instance of the java.lang.Class Class. Each of these examples can represent a Java class. An object of this class is created using the newInstance() method of this instance. The actual situation may be more complex, as Java bytecode may be dynamically generated by a tool or downloaded over a network.

The loading process of classLoader

The complete life cycle of a class from loading to virtual machine memory to unloading includes seven stages: class loading, validation, preparation, parsing, initialization, use, and unloading.

Verification, preparation, and resolution are collectively referred to as connections.

loading

Refers to the loading of class bytecode files into memory from various sources through the class loader

Bytecode sources: Common loading sources include.class files compiled from local paths,.class files compiled from JAR packages, real-time compilation from remote networks, and dynamic proxies

During the load phase, the JVM needs to do three things

  1. Gets the binary byte stream that defines a class by its fully qualified name
  2. Convert the static storage structure represented by this byte stream to the runtime data structure of the method
  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

Find the Class to load and place the Class information in the JVM’s method area, then instantiate a Java.lang. Class object in the heap as an entry point to the method area for that Class information.

validation

Ensure that the byte stream loaded in conforms to the virtual machine specification and does not cause security errors

  1. File format validation to see if there are irregularities or additional information in the file, such as unsupported constants in the constant
  2. Validation of metadata to ensure that the information it describes conforms to the Java language specification. For example, whether a class has a parent class, whether it inherits a final class that is not allowed, and so on
  3. Bytecode validation to ensure that the semantics of the program are reasonable, such as to ensure that the type conversion is reasonable
  4. Validation of symbol references, such as checking whether the corresponding class can be found by fully qualified name in symbol references, checking whether the accessibility (private, public, etc.) in symbol references can be accessed by the current class, etc

To prepare

The main thing is to allocate memory for class variables (note, not instance variables) and assign initial values.

Note: The initial value is not the initial value specified in the code, but the default initial value of the Java virtual machine based on different variable types, such as the initial value of int is 0, reference is null, etc

parsing

The process of replacing symbolic references in a constant pool with direct references.

For example, if you call hello() at 1234567, then hello is a symbolic reference and 123456 is a direct reference.

During the parsing phase, the virtual machine replaces all symbolic references to class names, method names, and field names with specific memory addresses or offsets, i.e. direct references

Initialize the

For the initialization phase, the Java Virtual Machine Specification specifies that there are only six cases in which classes must be “initialized” immediately (and loading, validation, and preparation naturally need to begin before that)

  1. When you encounter four bytecode instructions — New, getstatic, putstatic, or Invokestatic — if the type has not been initialized, you need to trigger its initialization phase first. Typical Java code scenarios that can survive four instructions are

    • When an object is instantiated using the new keyword
    • Read or set a static field of a type (except for static fields that are modified by final and have been put into the constant pool at compile time)
    • When a static method of a type is called
  2. When a reflection call is made to a type using the java.lang.Reflect package’s methods, initialization needs to be triggered if the type 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. If a Java lang. Invoke. The final solution analysis results for MethodHandle instance REF_getStatic, REF_putStaticREF_invokeStatic, REF_newInvokeSpecial handle four kinds of methods, If the class corresponding to the method handle has not been initialized, it needs to be initialized first

  6. When an interface defines a new JDK 8 default method (an interface method decorated with the default keyword), if any of the interface’s implementation classes are initialized, the interface should be initialized before it.

Although the classloader loading process has a complex 5 steps, the fact that all but four of them are controlled by the JVM doesn’t leave much room for intervention other than development to adapt to its specifications. Loading is the most important way we control the Classloader for a particular purpose

Classloader parent delegate mechanism

In the parent delegation model, the parent-child relationship between class loaders is not generally realized by Inheritance, but by Composition to copy the code of the parent loader.

From the perspective of Java virtual machines, there are only two different class loaders: one is the Bootstrap ClassLoader, which is implemented by C++ language and is part of virtual machines; The other part is all the other classloaders that are implemented in the Java language, exist independently of the virtual machine, and are all inherited from the abstract java.lang.ClassLoader (for HotSpot, and for some other virtual machines, the entire virtual machine itself is written in Java).

The parent delegation mechanism of a classloader refers to the problem of which loader loads a class when there is a parent-child relationship between multiple classloaders. Its specific process is as follows: When a class is loaded, it does not load, but delegates the load to its parent class, which in turn delegates the load to its parent class. Therefore, all the loading of the class will be entrusted to the top load, namely the Bootstrap Classloader, and Frey himself cannot complete the load request. The child loader will try to load it itself. Use parents delegation model, Java classes together with its loader has a hierarchy, with priority in this hierarchical model, can avoid class reloading, also can avoid the tired heart and by different class loaders loaded into a different memory conflict and confusion, so as to ensure the security of the Java core library.

Start the class loader

Bootstrap Classloader loads the class libraries identified by the VM in the <JAVA_HOME>/lib directory to the VM memory. We commonly used foundation classes, such as Java. Util., Java. IO., Java. Lang. * *, and so on are made by root loader loads

Extend the class loader

Extention Classloader is responsible for loading JVM extension classes such as the Swing family, built-in JS engines, XML parsers, etc

Application loader

The Application Classloader, also called the system Classloader, is responsible for loading the specified class libraries on the user’s path. It loads our own code and the third-party JAR packages we use

Custom loader

The Custom Classloader passes our Custom loader for some particular implementation

Application scenarios of classLoader

Class loaders can solve: class conflicts, implement hot loading and hot deployment, and even protect the encryption protection of JAR packages

Java. Lang. This class is introduced

The basic responsibility of this Class is to find or generate bytecode for a given Class name, and then define a Java Class, an instance of the java.lang.Class, from that bytecode. In addition, ClassLoader is responsible for loading resources required by Java applications, such as image files and configuration files. But this article only discusses its ability to load classes.

The methods in ClassLoader that are associated with loading classes

methods instructions
getParent() Returns the parent class loader of the class loader.
loadClass(String name) The load name isnameClass, and the return result isjava.lang.ClassClass.
findClass(String name) The lookup name isnameClass, and the return result isjava.lang.ClassClass.
findLoadedClass(String name) The lookup name isnameIs the class that has been loadedjava.lang.ClassClass.
defineClass(String name, byte[] b, int off, int len) The byte arraybIs converted to a Java class, and the result returned isjava.lang.ClassClass. This method is declared asfinal.
resolveClass(Class<? > c) Link to the specified Java class.

Break the parental delegation mechanism

The parental delegation model was “broken” three times in large scale

  1. The first “break” of the parental delegation model actually happened before the advent of the parental delegation model — long before JDK 1.2 came out. Since the parent delegate model was introduced after JDK 1.2, but the concept of classloaders and the abstract java.lang.ClassLoader were present in the first version of Java, facing the existing code for user-defined classloaders, Java designers had to make some compromises when introducing the parental delegation model. To accommodate the existing code, they could no longer technically avoid the possibility of loadClass() being overridden by subclasses. You can only add a new protected method, findClass(), to java.lang.classloader after JDK 1.2 and override it as much as possible when instructing user-written classloading logic instead of writing code in loadClass().

  2. The second “break” of the parental delegation model is caused by a flaw in the model itself. This mechanism is broken when the underlying type requires interfaces from other services. Examples are JNDI, JDBC, JCE, JAXB, and JBI

    The solution: Thread Context ClassLoader. This ClassLoader can be set using the setcontext-classloader () method of the java.lang.Thread class. If it has not been set when the Thread is created, it will inherit one from the parent Thread, if it has not been set globally in the application. This class loader is the application class loader by default.

  3. The third “break” of the parental delegation model is due to users’ desire for dynamic application, and by “dynamic” I mean some very “Hot” terms: Hot Swap, Hot Deployment of modules

    The key to OSGi’s modular deployment is the implementation of its custom class loading mechanism. Each program module (called a Bundile in OSGi) has its own class loader. When a Bundile needs to be replaced, the Bundle is replaced with a similar loader to implement the hot replacement code. In an OSGi environment, class loaders move away from the tree structure recommended by parental delegation and further develop into a complex network structure. When a class load request is received, OSGi will perform the class search in the following order

    • Delegate classes that begin with Java.* to the parent class loader
    • Otherwise, delegate the classes in the list to the parent class loader.
    • Otherwise, delegate the classes from the Import list to the class loader of the Export class’s Bundle.
    • Otherwise, look up the current Bundle’s ClassPath and load it using your own class loader.
    • Otherwise, it checks whether the class is in its own Fragment Bundle. If so, it delegates the load to the Fragment Bundle’s classloader.
    • Otherwise, look for the Bundle in the Dynamic Import list and delegate the load to the corresponding Bundle’s classloader.
    • Otherwise, class lookup fails.

    Only the first two points of the lookup order above still conform to the principles of the parent delegate model; the rest of the class lookup is done in the flat classloader.

Reference/Source: Understanding the Java Virtual Machine in Depth