Class loading mechanism of virtual machine:
The virtual machine loads the data describing the class from the class file into the memory, verifies, converts, parses and initializes the data, and finally forms a Java type (Java bytecode) that can be directly used by the virtual machine;
The complete declaration cycle of a class includes the following stages:
Load, connect (verify, prepare, parse), initialize, use, uninstall;
Class loading process
A blog has specifically introduced the class loading process, which will not be expanded in detail here. Loading is roughly to obtain the binary byte stream of the corresponding class through the fully qualified name of the class, and then convert the static storage structure into the dynamic storage structure at runtime to generate a class object as the entry of the method to access this class; The connection process includes verification, preparation and analysis; Verification is to check whether the bytecode file meets the requirements of the virtual machine, such as whether it starts with cafe babe, whether the data flow and control flow meet the rules, whether it will endanger the normal operation of the virtual machine, and whether the direct reference corresponding to the symbol reference exists (whether the reference relationship can be resolved normally, etc.), During initialization (clinit < >, not every class must exist, but only when there is a class static variable or static code block static), the assignment statements of static variables will be collected in the order of code for assignment operation; The use process is not described in detail; Class unloading: the conditions for judging whether a class can be unloaded are harsh, and even if it is met, it will not be unloaded. (1) there are no instances of this class in the virtual machine heap; (2) The class loader of the corresponding class has been unloaded; (3) There is no clazz object of this class, so it is guaranteed that this class cannot be accessed anywhere through reflection;
This article focuses on the following parental delegation models and how to destroy the parental delegation model?
First, what is the parental delegation model?
From the perspective of Java programmers, class loaders include three types: bootstrap classloader, which is responsible for loading Java_ Basic classes under home / lib; The extension class loader is responsible for loading Java_ Related classes under home / lib / ext, system class loader (application class loader). If there is no special class loader specified, classes are generally loaded through the system class loader;
Parental delegation model:
In addition to starting the class loader, any class loader must have its own parent class loader. When receiving a class loading request, first delegate the class loading request to the parent class loader. Only when the parent class loader cannot complete the loading, will the child class loader try to load;
It should be noted that the parent-child relationship of the class loader is realized in the form of composition, not inheritance;
Next, let's look at the principle of the parent delegation model through the source code:
private final ClassLoader parent; public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // Class loading requires obtaining locks first to prevent thread insecurity when multiple threads load classes concurrently synchronized (getClassLoadingLock(name)) { // See if the class has finished loading Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { // If the parent class loader exists and is not empty, first call the LoadClass of the parent class to try to load c = parent.loadClass(name, false); } else { // If the parent class is empty, it proves that it is the boot class loader. Call the boot class loader to load c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // not found exception catch } } if (c == null) { // If c is still empty, it means that there is no way for the parent class to complete the loading. Try to load the class yourself c = findClass(name); } } if (resolve) { resolveClass(c); } }
The whole logic is similar to that written in the comment. When the parent class loader is not empty, it will continue to pass up and give it to the parent class loader to complete the class loading. Only when the parent class cannot be loaded, it will be handed over to the child class to try to load, and the child class tries to load this class using
findClass() method, so usually, the subclass loader only needs to inherit the ClassLoader, and then rewrite the findClass method to complete the class loading according to the mechanism of the parent delegation model. If the parent class cannot be loaded, it will naturally call its own rewritten findClass method to load the class;
Let's take a file system classloader as an example to see how to implement a custom classloader;
public class FileSystemClassLoader extends ClassLoader{ private String rootDir; public FileSystemClassLoader(String rootDir) { this.rootDir = rootDir; // Save the system path initialization of the class } protected Class<?> findClass(String name) throws ClassNotFoundException { // The subclass loader loads the class by overriding the findclass method byte[] classDate = getClassData(name); // Gets the binary byte class data loaded by the class if (classDate == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classDate, 0, classDate.length); // Call defineclass to load the class } }
// Load the class from the file public byte[] getClassData(String className) { String path = classNameToPath(className); try { InputStream in = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead; while (bytesNumRead = in.read(buffer) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } }
Finally, what should I do if I want to break the class loading mechanism of parental delegation?
By analyzing the source code, we find that in fact, the logic of parental delegation is implemented in loadClass(). If we want to destroy the parental delegation model, we only need to let the custom class loader implement the ClassLoader class, and then rewrite the loadClass() method, we can load the class without following the parental delegation mechanism!