This is the 19th day of my participation in the August Wenwen Challenge.More challenges in August

preface

In the original article, we discussed the classloader process, but we didn’t go into the inner details. In this article, we will look at what happens at each step of the classloader process.

The process of class loading is as follows: load, validate, prepare, initialize, parse, use, unload. The key steps to pay attention to are the previous five steps. These details are the content of the eight-part essay, so this article mainly focuses on simple summary and induction.

An overview of the

This article focuses on the loading process of class loading, which includes the first five steps and detailed details.

The process of class loading

Let’s break down these five steps and talk about what each step does:

loading

The first step is loading, and what loading does is when the JVM needs to find the.class object, and we all know that the.class object is not loaded twice if it’s in the method area, so when does the class get initialized?

The most obvious thing to think of is new, so you can be sure that new will be the trigger condition. Public static String mind = “XXX”; public static String mind = “XXX”; Static methods using other classes must also trigger classloading conditions. So these are the three conditions, the three that we think of the most, and the next one is a little bit more complicated for triggering the load action.

In addition to the new, we also know that one way is through the Java reflection mechanism, is actually getting. The corresponding class file class loader to generate a class, reflection tool is to simplify this one action, so it can guess, if reflect class also needs to be loaded is not method, it must also want to load first class loading came in.

We realized inheritance, and from the two angles to consider when it loads, from the perspective of inheritance, if a parent is not loaded, then the parent class is to be loaded in, as to why you must use the parent class, the class constructor can be used as a solution, we all know that in the constructor method will execute a super () implicit method, The reason why super() is called is because all classes have Object as their parent. Also due to the design of the JVM’s classloader, the parent delegate mechanism dictates that all subclasses need to be loaded before being loaded. (Note that it is only loading, whether it needs to be initialized or not will be discussed below)

Finally, let’s take a look at the improvements made due to the JDK version. The first is the JDK7 dynamic language support, which triggers loading of all classes that involve new or use static property directives. In JDK8, the default method of the interface is introduced so that the interface can also do the “abstract class” thing, so if a subclass implements the interface with the default method, it also needs to be loaded.

Let’s summarize the above “initialization” conditions for loading:

  • New, static field references, static method references
  • An inherited parent class is loaded if a field or method defined by the parent class is used, but its subclass is not loaded. But if it is but if it’s calling a subclass, the parent class must be loaded.
  • Classes generated by the reflection mechanism need to be loaded (otherwise they cannot be reflected).
  • The JDK7 dynamic language involves directives related to new and static
  • Jdk8 implements classes with interfaces for default methods.

Finally, take a look at the code that loads the “initialization” in the book. The result is a bit unexpected:

package org.fenixsoft.classloading;
/** * Passive use of class fields demo 1: * Referencing a static field of a parent class by a subclass does not cause the subclass to initialize **/
public class SuperClass {
	static {
		System.out.println("SuperClass init!");
	}
	public static int value = 123; 
}
public class SubClass extends SuperClass {
	static {
		System.out.println("SubClass init!"); }}/** * not actively using class fields to demonstrate **/
public class NotInitialization {
	public static void main(String[] args) { System.out.println(SubClass.value); }}/* SuperClass init! * /

Copy the code

Let’s look at what happens if we call only the static methods of a subclass:

static class superClass{
    static {
        System.out.println("super load"); }}static class SubClass extends superClass{
    static {
        System.out.println("sub load");
    }

    public static void test(a){
        System.out.println("sdsad"); }}public static void main(String[] args) {
 // write your code here
    SubClass.test();
}/** Run result */
Copy the code

validation

Validation is followed by a step after class loading, verify the main do is to test whether the current class file can be accepted by the virtual machine, this step is crucial step, to determine whether the virtual machine safety, so it took more than N virtual machine specification which the content of the page, of course the test content is also introduced. :

The first is to validate file formats, such as magic numbers, primary and secondary versions, constant pools, and indexes, to prevent someone from tampering with the class file structure.

The validation format is followed by the validation of specific data, such as whether the class has a parent class, and the validation of defined fields and attributes that conform to Java syntax. This section is also called metadata validation and can be understood simply as validation for syntax and so on.

After that comes bytecode verification, which is also the most complicated step. Because the operation of the program depends on the program counter to scan bytecode instructions, the main content of bytecode verification is to verify the “atomicity” of the operation, such as Int operation will not change into long operation, and ensure the normal operation of the stack frame method.

Finally, the symbolic reference validation verifies whether the fully qualified name of the class can be found through the symbolic reference and whether the field is accessible.

In general, the verification stage is divided into the following parts:

  • File format validation
  • Metadata validation
  • Bytecode verification
  • Symbolic reference verification

To prepare

One of the most misunderstood phases, the important thing to note in this phase is that the static variables initialized in the preparation phase are static constants of type final. Additional preparation for class variable is initialized, but won’t appear the instantiation of class, that is generated at this time is just a stack of references, can quickly build objects by reference in the initialization phase but only made a preparation, in addition to note jdk7 actually due to the internal have been secretly move the constant pool to heap, So these variables are all generated in the heap.

Here are the initialization details for static variables:

private static final int count1 = 123;
private static int count2 = 55;
Copy the code

So let’s just say the result, count is 123 at this stage, and count2 is 0. This detail also explains why many books recommend using final fields as much as possible, because it brings the “initialization” step forward. It adds up to a nice performance boost.

parsing

The core of parsing is to turn a symbolic reference into a direct reference. What is a symbolic reference and what is a direct reference? With a long book description, understood here in a case to show (see the code below), obj can be regarded as a symbolic reference, reference symbols can be any unambiguous “placeholder” (and keyword conflict is not, of course), and the direct reference is pointed to the placeholder instance in a heap, Having a direct reference also proves that the instance has space in memory, so a direct reference must be a pointer to an actual address in the heap:

public void test(a){
  Object obj = null;
  obj = new Object();
}
Copy the code

There is one exception: the Invokedy namic instruction. This is an instruction that Java created to support dynamic language features, and all other instructions are considered “static” in the parsing step, meaning that the address to which the pointer points has been determined.

The parse action is mainly for class or interface, field, class method, interface method, method type, method handle, and call point qualifier.

The details of parsing are summarized here, but there are only four main steps to understand:

The class loader loads an array, parses each element of the fully qualified name, and generates an access object corresponding to the array dimension. 2. Field parsing this class lookup parsing interface parent class check parent class check throws nosuckField 3. If the class_index index of the class is an interface, an exception will be raised. This class finds the parent class. This class finds the parent class and the interface. Interface method resolution is similar to method resolution, noting that the resolution ends with the Object classCopy the code

Initialize the

Note that this step is the actual initialization that the programmer recognizes, the sequence of initializations learned when learning the basics of Java, and the actual execution of Java code, so the term “allocate resources” might be more appropriate.

As mentioned earlier, there is a build of a class variable in the preparation phase, which you can think of as the.class object being loaded into the method area, and initialization actually builds the reference to the method area onto the heap.

The

() method is generated by Java at compile time and can be thought of simply as the entry point to a class’s constructor. All class initializations must call this method, and if the parent class is not initialized, the parent class’s

() is executed. Finally, in the case of an interface, < Clinit >() is called when the interface’s constants are used, and the interface’s implementation class does not execute the interface’s < Clinit >() methods when initialized.

Class loading details

Knowing the class loading process, this section will fill in some of the class loading details:

Class loading basics

  • The load/verify/prepare sequence is deterministic and atomized

The Jvm only guarantees order, but not consistency, meaning that they can be interspersed with other operations

  • Parsing may occur before and after initialization

This is set up to satisfy the dynamic binding feature

  • Loading validation, preparation is not done synchronously, there will be cross-allowed situations

The order is fixed, but not synchronized.

What is a passive quote?

  • A subclass reference to a superclass definition field only triggers superclass initialization

  • Array initializers are newarray, not legal object initializers

  • Constant pool elements that are final modified

conclusion

This paper mainly focuses on the process of class loading, which is reviewed. It can be seen that the process of class loading is relatively easy to understand. One of the contents that needs special attention is the preparation stage and the initialization stage, which may also be a pit point.

Write in the last

The next article will continue to delve into classloaders and parental delegation, as mentioned earlier in the series. The next section will delve into and extend classloaders content and the impact of JDK9 modularity on classloaders.