JVM class loading mechanism (6)

1. Classification and working principle of class loader

The class loader (ClassLoader) is an important part of the Java virtual machine (JVM), responsible for loading the bytecode of the class into memory and converting it into an executable Java class. The main task of the class loader is to find the bytecode file according to the name of the class and load it into the memory, then perform operations such as verification, preparation and analysis, and finally generate an executable class.

Classification of class loaders:

  1. Bootstrap Class Loader (Bootstrap Class Loader): Responsible for loading the Java platform core library, located in <JAVA_HOME>/jre/libthe directory . It is a part of the virtual machine itself, implemented by C++, not a Java class, and is mainly responsible for loading the core class library, such as the classes in java.langthe package .

  2. Extension Class Loader (Extension Class Loader): Responsible for loading the extension library of the Java platform, located in <JAVA_HOME>/jre/lib/extthe directory . It is a class loader implemented by Java that loads JAR files in <JAVA_HOME>/jre/lib/extthe directory .

  3. Application Class Loader (Application Class Loader): Also known as the System Class Loader (System Class Loader), responsible for loading the class library specified on the application's class path (Classpath). It is a developer-defined class loader and the default implementation of the Java class loader.

  4. User-defined class loader: In addition to the above-mentioned built-in class loaders, developers can also customize class loaders, inherit from java.lang.ClassLoaderthe class , and implement their own class loading logic.

        The working principle of the class loader: The class loader uses the parent delegation model (Parent Delegation Model) to load classes, that is, when a class loader receives a request to load a class, it first delegates the loading task to the parent class loader, Only when the parent class loader cannot load the class, it will be loaded by the current class loader itself.

The specific workflow is as follows:

  1. When a class loader receives a request to load a class, it first checks to see if the class has already been loaded. If it has already been loaded, return the loaded class directly.
  2. If the class has not been loaded before, the loading task is delegated to the parent class loader. The parent class loader checks to see if the class has already been loaded in the same way.
  3. If the parent class loader is also unable to load the class, the current class loader will try to load it itself. It first finds the class file, reads the bytecode, and creates the corresponding Class object according to the bytecode.
  4. If the current class loader still cannot load the class, a ClassNotFoundException will be thrown.

        Through the parental delegation model, it can be guaranteed that the loading of classes is a top-down hierarchical relationship, ensuring the uniqueness and consistency of classes. This hierarchical relationship makes different class loaders responsible for loading different classes, realizing class isolation and security.

2. Parent delegation model

        The parent delegation model (Parent Delegation Model) is a class loading mechanism that defines the hierarchical relationship and loading order between class loaders. According to this model, when a class loader receives a request to load a class, it first delegates the loading task to its parent class loader, and only when the parent class loader cannot load the class, the current class loader itself does it. load.

        The main purpose of the parent delegation model is to ensure the uniqueness and consistency of classes. Through this model, it is possible to avoid conflicts of classes with the same name in different class loaders, and to ensure that classes are loaded in a top-down hierarchical relationship.

        The advantage of the parental delegation model is that it ensures the consistency and security of class loading. It can avoid conflicts of classes with the same name, and can ensure the security of the core class library, preventing user-defined classes from replacing classes in the core class library.

However, sometimes we need to break the parental delegation model, customize the class loader and load specific classes in it. Some common examples include:

  1. In some containers or frameworks, it is necessary to load a specific version of the class library instead of using the version in the system class library. At this time, you can customize the class loader to give priority to loading a specific version of the class library.
  2. When implementing the code hot replacement (HotSwap) function, it is necessary to dynamically load the modified class at runtime. At this time, you can customize the class loader to load the updated class file.
  3. In some special-demand scenarios, you can customize the class loader to implement specific loading logic, such as loading class files from a database or network.

        By customizing the class loader and breaking the parent delegation model, we can achieve a more flexible class loading method and meet specific needs. However, it should be noted that breaking the parental delegation model may introduce uncertainty and security risks in class loading, so it needs to be used with caution.

3. The process of class loading

        Class loading is the process of loading a class's bytecode file into memory and converting it into an executable Java class. Class loaders are responsible for performing class loading operations, and there are certain relationships and collaborations between them.

The process of class loading usually includes the following steps:

  1. Loading: Find and read the bytecode file of the class, usually from the file system, JAR file or network. The result of loading is to generate a corresponding bytecode representation in memory, usually in the form of a byte array.

  2. Verification: Verify the correctness and security of the bytecode. Checks whether the bytecode complies with the JVM specification, contains illegal opcodes or structures, violates security constraints, and more.

  3. Preparation: Allocate memory and set default initial values ​​for static variables of the class. These variables include static fields and constants, which are allocated memory space at this stage, but have not yet been assigned initial values.

  4. Resolution: Convert symbolic references to direct references. A symbolic reference refers to the symbolic name of a class, method, field, etc. used in the bytecode, while a direct reference refers to a pointer or handle in memory. The parsing process resolves symbolic references into direct references, enabling the virtual machine to directly access corresponding classes, methods, fields, and so on.

  5. Initialization: Initialize the class, including performing static variable assignment and initialization of static code blocks. At this stage, the static variables of the class are given real initial values ​​and the static code block is executed.

4. The realization principle of dynamic loading and dynamic proxy

        Dynamic loading and dynamic proxy are commonly used technologies in Java, and they are all implemented based on the reflection mechanism.

  1. Dynamic loading (Dynamic Loading): Dynamic loading refers to dynamically loading classes or resources as needed when the program is running, rather than determining at compile time. Through dynamic loading, specific classes can be loaded according to conditions at runtime to achieve dynamic expansion and flexibility of classes.

        The implementation principle of dynamic loading mainly depends on ClassLoaderthe class and its subclasses. By customizing the class loader, you can specify how and where to load classes, thereby achieving dynamic loading. The general steps are as follows:

  • Create a custom class loader and override its findClassmethod to implement the logic of class loading.
  • When loading a class, determine whether dynamic loading is required, and if so, call defineClassthe method to convert the bytecode of the class into Classan object and return the object.
  • When using a dynamically loaded class in a program, invoke its method or access its property through reflection.
  1. Dynamic Proxy (Dynamic Proxy): Dynamic Proxy is a design pattern that allows a proxy class to be created at runtime to replace the original class in order to control and extend the original class. Dynamic proxies are often used to implement scenarios such as AOP (aspect-oriented programming).

The implementation principle of dynamic proxy mainly depends on the classes and interfaces in the Java java.lang.reflectpackage . The general steps are as follows:ProxyInvocationHandler

  • Define an interface and declare the methods that both the proxy class and the original class must implement.
  • Create a proxy handler class that implements InvocationHandlerthe interface , overriding its invokemethod in which you define enhancement logic to the original class method.
  • Create a proxy object through Proxy.newProxyInstancethe method , and pass in the proxy processor object and the interface to be proxied.
  • When the method of the proxy object is called, invokethe method , thereby executing the enhanced logic of the original class method.

        The dynamic proxy replaces the original object through the proxy object and intercepts the call to the method of the original object, so as to realize the control and extension of the original class. Through the dynamic generation of proxy objects and the processing of invoking processors, the function of dynamic proxy is realized.

Guess you like

Origin blog.csdn.net/zz18532164242/article/details/130856563