Class Loading Mechanism, Parent Delegation Model

Generally speaking, we divide the class loading process of java into three main steps: loading, linking and initialization. The first stage is the loading stage. It is java that reads bytecode data from different data sources into JVM and maps it to JVM-approved data structures, and maps it to JVM-approved data structures (Class objects). The data sources here may be various forms, such as jar files, class files, or even network data sources. If the input data is not a ClassFile structure, ClassFormatError is thrown. The second stage is Linking, which is the core step. Simply put, the original class definition information is smoothly transformed into the process of running JVM. There are three further steps for a new atmosphere:

  • Verification is an important guarantee for the security of virtual machines. JVM needs to verify that byte information conforms to the Java Virtual Machine specification. Otherwise, it is considered to be Verify Error. In this way, malicious information or irregular information is placed to endanger the operation of JVM, and the verification phase may trigger more class loading.
  • Prepare, create static variables in classes or interfaces, and initialize the initial values of static variables. There is a difference between the "initialization" here and the explicit initialization stage below. The emphasis is on allocating the required memory space and not going back to execute further JVM instructions.
  • Resolution, in which symbolic reference s in the constant pool are replaced by direct references asynchronously. In the Java Virtual Machine specification, the analysis of classes, interfaces, methods and fields is introduced in detail. The last step is initialization, which really executes the code logic of class initialization, including the action of astonishing field assignment. The first step executes the logic in the static initialization block defined by the class. The compiler will organize the logic in the compilation stage, and the initialization logic of the parent type takes precedence over the initialization logic of the class. Logic of the current type. Let's talk about the Parent Delegation Model. Simply put, when a Class_Loader tries to load a certain type, it tries to delegate the whole task to the parent loader of the current loader unless the parent loader can't find the corresponding type. The purpose of using the delegation model is to avoid overloading Java types.
package com.wzl.day11;

/**
 * @author wuzhilang
 * @Title: Day11
 * @ProjectName questions
 * @Description: TODO
 * @date 8/27/20196:27 PM
 */

/**
 * Compile and decompile:
 * Command: Javac Day11.java
 * Javap –v Day11.class
 */
public class Day11 {
	public static int a = 100;
	public static final int INT_CONSTANT = 1000;
	public static final Integer INTEGER_CONSTANT = Integer.valueOf(10000);
}

Function

javac  -encoding UTF-8 Day11.java
Javap –v Day11.class

The output is

Classfile /D:/questions/question/src/com/wzl/day11/Day11.class
  Last modified Aug 27, 2019; size 471 bytes
  MD5 checksum c71fc3ab252eb1585d635d591ac61712
  Compiled from "Day11.java"
public class com.wzl.day11.Day11
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #5.#22         // com/wzl/day11/Day11.a:I
   #3 = Methodref          #23.#24        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #4 = Fieldref           #5.#25         // com/wzl/day11/Day11.INTEGER_CONSTANT:Ljava/lang/Integer;
   #5 = Class              #26            // com/wzl/day11/Day11
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               a
   #8 = Utf8               I
   #9 = Utf8               INT_CONSTANT
  #10 = Utf8               ConstantValue
  #11 = Integer            1000
  #12 = Utf8               INTEGER_CONSTANT
  #13 = Utf8               Ljava/lang/Integer;
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               <clinit>
  #19 = Utf8               SourceFile
  #20 = Utf8               Day11.java
  #21 = NameAndType        #14:#15        // "<init>":()V
  #22 = NameAndType        #7:#8          // a:I
  #23 = Class              #28            // java/lang/Integer
  #24 = NameAndType        #29:#30        // valueOf:(I)Ljava/lang/Integer;
  #25 = NameAndType        #12:#13        // INTEGER_CONSTANT:Ljava/lang/Integer;
  #26 = Utf8               com/wzl/day11/Day11
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/Integer
  #29 = Utf8               valueOf
  #30 = Utf8               (I)Ljava/lang/Integer;
{
  public static int a;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC

  public static final int INT_CONSTANT;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 1000

  public static final java.lang.Integer INTEGER_CONSTANT;
    descriptor: Ljava/lang/Integer;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  public com.wzl.day11.Day11();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 16: 0

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        100
         2: putstatic     #2                  // Field a:I
         5: sipush        10000
         8: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        11: putstatic     #4                  // Field INTEGER_CONSTANT:Ljava/lang/Integer;
        14: return
      LineNumberTable:
        line 17: 0
        line 19: 5
}
SourceFile: "Day11.java"

As you can see, common primitive static variables and reference types (even variables) require additional calls to jvm instructions such as putstatic, which are executed in the explicit initialization phase rather than in the preparatory phase; while primitive variables do not require such steps.

  • If you really want to understand the Parent Delegation Model, you need to understand the architecture and responsibilities of class loaders in java, at least what built-in class loaders are there.
  • From the application point of view, to solve some class loading problems, such as my java program started slowly, is there any way to minimize the cost of Java class loading?
# Specify a new bootclasspath to replace the internal implementation of the java. * package
java -Xbootclasspath:<your_boot_classpath> your_App
# a means append, adding the specified directory after bootclasspath
java -Xbootclasspath/a:<your_dir> your_App
# p means prepend, adding the specified directory before bootclasspath
java -Xbootclasspath/p:<your_dir> your_App

Usage is actually easy to understand, for example, using the most common "/p", since it is the front-end, there is an opportunity to replace the implementation of individual base classes. Generally, we can use the following method to get the class loader, but in the usual JDK/JRE implementation, the extended class loader getParent() can only return null.

public final ClassLoader getParent()
  • Extension or Ext Class-Loader is responsible for loading the content of our most familiar classpath. There is a confusing concept here, system class loader, which is usually built-in application class loader in JDK by default, but it is also possible to modify, such as:
java -Djava.system.class.loader=com.yourcorn.YourClassLodader HelloWorld

If we specify this eucalyptus, the built-in application class loader in JDK will be able to customize the parent class of the loader, which is often used in scenarios where parental delegation patterns need to be changed.

The specific operation is as follows: Generally, the class loading mechanism has three basic characteristics:

  • Parent Delegation Model. But not all class loads follow this model. Sometimes it is possible to load user code by starting the type of class loader, such as the Service Provider/Service Loader mechanism in JDK. Users can provide their own implementations on the standard API framework. JDK also needs to provide some default reference implementations. For example, many aspects of JAVA, such as JNDI, JDBC, file system, Cipher and so on, are utilizing this mechanism. In this case, instead of using the parent delegation model to load, they use the so-called context loader.
  • Visibility, subclass loaders can access the type of parent loader, but the reverse is not allowed. Otherwise, due to the lack of necessary isolation, we can not use class loaders to implement container logic.
  • Uniformity, since the type of parent loader is visible to the child loader, the type loaded in the parent loader will not be repeated in the child loader. Note, however, that the same type can still be loaded multiple times between class loaders'"neighbors" because they are not visible to each other. -- To be continued

Keywords: Java jvm JDK network

Added by tharagleb on Tue, 27 Aug 2019 15:51:40 +0300