preface

Do you really understand Java’s classloading mechanism? If you can do this, you probably already know and understand the Java class loading mechanism. If the result is unexpected, it is necessary to understand the Java class loading mechanism. The following code

package com.jvm.classloader;

class Father2{
    public static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father static code block"); }}class Son2 extends Father2{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println(Son static code block);
    }
}

public class InitativeUseTest2 {
    public static void main(String[] args) { System.out.println(Son2.strSon); }}Copy the code
Father static block Son static block HelloJVM_SonCopy the code

Uh huh? In fact, the above procedure is not the key, may really not be difficult to everybody, might as well do the following interview question? If you get all the following interview questions right, then yes, you don’t need to read this article, really.

package com.jvm.classloader;

class YeYe{
    static {
        System.out.println("YeYe static code block"); }}class Father extends YeYe{
    public static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father static code block"); }}class Son extends Father{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println(Son static code block);
    }
}

public class InitiativeUse {
    public static void main(String[] args) { System.out.println(Son.strFather); }}Copy the code

Let’s take a guess at what the results will be…

Pay attention to… Pay attention to… Pay attention to…

YeYe static code block Father static code block HelloJVM_FatherCopy the code

Is right is wrong already have a number of bar, I will not expose your small mind…

The above interview questions are typical Java class loading questions, if you do not understand the Java loading mechanism, then you may be wrong about the above two questions. This article will introduce the Java class loading mechanism, so that you can understand the Java class loading mechanism.

In fact, the blogger still wants to give a question, after all, you have already had the foundation of the first two questions, so look at the code:

package com.jvm.classloader;

class YeYe{
    static {
        System.out.println("YeYe static code block"); }}class Father extends YeYe{
    public final static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father static code block"); }}class Son extends Father{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println(Son static code block);
    }
}

public class InitiativeUse {
    public static void main(String[] args) { System.out.println(Son.strFather); }}Copy the code

Pay attention, pay attention, pay attention

Result: HelloJVM_FatherCopy the code

Impulsive small white children shoes to see the results of the operation, the decisive cancellation of the blog account….

1. What is class loading (class initialization)

One important area of the JVM is class loading

When a program actively uses a class that has not already been loaded into memory, the JVM initializes the class through three steps: load, connect, and initialize. If there are no accidents, the JVM will complete three steps in a row, so these three steps are sometimes referred to collectively as class loading or class initialization.

Class loading involves class loaders, so let’s take a look at class loading.

Class loading (class initialization) :

1. In Java code, type loading, concatenation, and initialization are all done at runtime (the three stages a class goes through when it is loaded from disk to memory).

 

2, provides greater flexibility, increases more possibilities

Although the first sentence above is very short, the amount of knowledge it contains is huge! Contains two important concepts:

1, type,

A defined class, interface, or enumeration is called a type without reference to an object and, during class loading, is some information prior to the creation of an object

2. During program operation

A typical example of this is dynamic proxy. In fact, many languages complete loading at compile time, and this feature gives Java programs greater flexibility and more possibilities

1. Precautions for class loading

1, the class loader does not need to wait until a class is “active for the first time use” reload it ~ about active for the first time to use this important concept will be explained below ~ 2, the JVM specification allows the class loader is expected that a class will be preloaded it when using 3, if met. In the process of preloaded class file missing or wrong, The class loader must not report a LinkageError until the program first actively uses the class. If the class is never actively used by the program, the class loader will not report an error.

First of all to you hit a precautionary shot: may not understand the JVM of children’s shoes may see very confused, feeling is all theoretical feeling, not reluctantly a word by word “dead look”, as long as to achieve a conceptual impression of good! Wait until there is a certain understanding of understanding and then look back a lot of good, after all, learning is a progressive process, remember there is no shortcut!

2. Class lifecycle

The load, validate, prepare, initialize, and unload phases are in a fixed order, and the loading process of a class must begin in this order (note “start”, not “proceed”), while the parsing phase may not: It can be started after initialization in some cases in order to support runtime binding in the Java language.

2. 1. Load

As mentioned above, the load phase is the first phase of class loading! The loading process of a class starts at the loading stage

The loading phase refers to reading the binary data from the class’s.class file into memory, placing it in the methods section of the runtime data section, and then creating a java.lang. class object in the heap (the JVM specification does not specify where the class object is located; the HotSpot VIRTUAL machine places it in the methods section). Used to encapsulate a class’s data structure within a method area. The end product of Class loading is the Class object in the heap, which encapsulates the Class’s data structure in the method area and provides the Java programmer with an interface to access the data structure in the method area.

A lot of people make the mistake that Class objects are stored in the heap, not the method. It is the metadata of a class that exists in the method area. Metadata is not the Class object of a Class. The Class object is the final product of loading. The method code, variable names, method names, access permissions, return values, and so on are all in the method area.

JDK7 creates Class instances in the heap; The JavaObjectsInPerm parameter in JDK7 is fixed to false. In JDK8, the permanent generation is removed and the method area is implemented in a meta-space instead, creating Class instances that remain in the Java Heap

When you write a new Java class, the JVM compiles it into a class object and stores it in a.class file of the same name. At run time, when an object of this class needs to be generated, the JVM checks to see if the class has been loaded into memory. If not, load the. Class file into memory. If loaded, instance objects are generated from the class file.

How do you understand the relationship between Class objects and objects that come out of new?

The object out of new takes car as an example. The Class of a car can be regarded as a concrete person, while the new car is a character image. The concrete person (Class) is unique, and the character image (new car) is multiple. Each character image in the mirror is created according to the specific person, that is, each new object is based on the Class template reference out! Why can refer to pinch? Because the Class object provides an interface to access the data structures in the method area.

Forget referring to the following picture to understand it, understanding is second, the point is to say that this sister hit pretty good-looking.

Summary: The loading phase is simply:.class file (binary data) — > read into memory — > put the data into the method area — > create the corresponding class object — > provide the interface to access the method area

Compared to other phases of class loading, the loading phase (specifically, the action of the loading phase to retrieve the binary byte stream of the class) is the most controllable, because developers can either use the system-provided class loader to complete the loading, or they can customize their own.

After the loading phase is complete, the binary byte streams outside the virtual machine are stored in the method area in the format required by the virtual machine, and an object of java.lang.Class is created in the Java heap through which the data in the method area can be accessed.

How to load.calss files: Classes are loaded by classloaders, usually provided by the JVM and the basis on which all previous programs are run. These classloaders provided by the JVM are often referred to as system classloaders. In addition, developers can create their own classloaders by inheriting the ClassLoader base class. Using different class loaders, you can load binary data of a class from different sources. Binary data usually comes from one of the following sources:

(1) Load directly from the local system. (2) Download. Class files over the network. (3) Load. Class files from zip, JAR archives. (4) Extract. Class files from a dedicated database. (5) Dynamically compile Java source files into. Class files

2. Verification

Validation: To ensure the correctness of the classes being loaded. You don’t have to go into much detail about validation, but you do need to understand the Class loading mechanism and that validation is all about making sure that the byte stream of a Class file contains the information required by the current virtual machine. So the following content about verification as understanding!

Validation is the first phase of the connection phase. The purpose of this phase is to ensure that the information contained in the byte stream of the Class file meets the requirements of the current VIRTUAL machine and does not compromise the security of the virtual machine. In the verification stage, four stages of inspection actions will be roughly completed:

File format verification: verify whether the byte stream conforms to the Class file format specification; For example, whether the value starts with 0xCAFEBABE, whether the major and minor versions are within the processing range of the current VM, and whether the constants in the constant pool have unsupported types.

Metadata verification: Semantic analysis of the information described by bytecode (note: compared with the semantic analysis in the compilation phase of JavAC) to ensure that the information described conforms to the requirements of the Java language specification; For example, does this class have a parent other than java.lang.object?

Bytecode validation: Determine that program semantics are legitimate and logical through data flow and control flow analysis.

Symbol reference validation: Ensures that the parse action is performed correctly.

The validation phase is important, but not required, and has no effect on program runtime. If the referenced classes are repeatedly validated, consider using the -xVerifyNone parameter to turn off most of the class validation to shorten the virtual machine class load time.

2, 3. Preparation [Key points]

Once the bytecode files have been validated, the JVM starts allocating memory for class variables and initializing them. The preparation phase is the phase that formally allocates memory and sets initial values for class variables, all of which will be allocated in the method area.

Two key points to note here are the object that memory is allocated and the type of initialization.

== class variable ==; static variable ==; all other variables are class member variables ==. In the preparation phase, the JVM allocates memory only for class variables, not for class member variables. Memory allocation for class member variables does not begin until initialization (which is covered below).

For example, in the preparation phase, the following code only allocates memory for the LeiBianLiang property and not for the ChenYuanBL property.

public static int LeiBianLiang = Awesome!;
public String ChenYuanBL = "jvm";
Copy the code

Type of initialization: During the preparation phase, the JVM allocates memory for class variables and initializes them (the JVM allocates memory only for class variables, not for class member variables, which of course cannot be initialized at this time). == But initialization refers to assigning a variable the default value of the data type in the Java language, not the value initialized in user code. = =

For example, after the preparation phase of the following code, the value of LeiBianLiang will be 0 instead of 666.

public static int LeiBianLiang = Awesome!;
Copy the code

Attention!! Attention!! Attention!!

But if a variable is a constant (modified by static final), the property is given the desired value during the preparation phase. For example, after the preparation phase, the ChangLiang value will be 666 instead of 0.

public static final int ChangLiang = Awesome!;
Copy the code

Static final is copied directly, and static variables are given default values for Java language types. We can figure it out with a little thought.

The difference between the two statements is that one has the final keyword modifier and the other does not. The final keyword in Java stands for immutable, meaning that the value of ChangLiang will not change once it is assigned. Since once a value is assigned it cannot be changed, it must be given the desired value by the user in the first place, so a class variable modified with final is given the desired value in the preparation phase. A class variable that is not modified with final may change during initialization or runtime, so there is no need to give it the desired value during preparation.

If you do not have a clear understanding of the keywords final and static, I suggest you read the following article organized by the blogger, hoping to help you!

Java Static, final, and Static final

2, 4

After passing through the preparation phase, it enters the parsing phase. In the parsing phase, the VIRTUAL machine replaces symbolic references in the constant pool with direct references. The parsing action is mainly performed for class or interface references, fields, class methods, interface methods, method types, method handles, and call point qualifiers. A symbolic reference is a set of symbols that describe a target, which can be any literal.

A direct reference is a pointer to a target directly, a relative offset, or a handle to the target indirectly.

== In fact, this stage is also almost transparent to us, it is good to understand ==.

2. 5. Initialization

It is during the initialization phase that the user-defined Java program code actually begins to execute.

Java programs can use classes in two ways: active and passive. In general, class initialization occurs only when the first active use of == on a class’s ==, so active use is also referred to as the time when “initialization” begins during class loading. What is active use? Active use of classes includes the following six types:

1. Create an instance of the class, the new way

 

Access to, or assign to, a static variable of a class or interface (except for static fields that are modified by final or, more accurately, by the compiler that put the result into the constant pool)

 

3. Call the static method of the class

 

4. Reflection (e.g. Class.forname (” com.gx.yichun “))

 

5. If a subclass of a class is initialized, its parent class is initialized as well

 

Classes marked as startup classes (JavaTest) and classes with the Main method are initialized first

 

Finally, note that for static fields, only the class that directly defines the field is initialized (executes a static block of code). This is especially true in inheritance and polymorphism! In order to facilitate understanding, the following will be explained by examples

2. 6. Smart water bottle

When the JVM completes the initialization phase, the JVM starts executing the user’s program code from the entry method. This use phase is also just to understand it.

2. 7. Uninstall

When the user program code is finished executing, the JVM starts destroying the created Class object, and eventually the JVM responsible for running it exits memory. This uninstall phase is just a look at it.

2. 8. End life cycle

The Life cycle of a Java virtual machine ends in one of the following situations

1. Execute system.exit () method

2. The normal execution of the program ends

3. The program is terminated abnormally due to an exception or error encountered during execution

4. The Java VM process is terminated due to an operating system error

3. Loading process of interface

The interface loading process is slightly different from the class loading process.

== When a class is initialized, all its parents are required to be initialized. However, when an interface is initialized, all its parents are not required to be initialized. When the parent interface is used, it is initialized. = =

4. Solve the opening interview question

package com.jvm.classloader;

class Father2{
    public static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father static code block"); }}class Son2 extends Father2{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println(Son static code block);
    }
}

public class InitativeUseTest2 {
    public static void main(String[] args) { System.out.println(Son2.strSon); }} Father static code block Son static code block HelloJVM_SonCopy the code

Son2. StrSon: Son2. StrSon: Son2. StrSon: Son2. StrSon: Son2. StrSon: Son2. StrSon: Son2.

Let’s look at the second problem from the beginning

package com.jvm.classloader;

class YeYe{
    static {
        System.out.println("YeYe static code block"); }}class Father extends YeYe{
    public static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father static code block"); }}class Son extends Father{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println(Son static code block);
    }
}

public class InitiativeUse {
    public static void main(String[] args) { System.out.println(Son.strFather); }} YeYe static code block HelloJVM_Father static code blockCopy the code

This one is a little bit tricky, but if you understand the passage, it’s pretty easy. What do you notice about this problem? Note that the Son subclass is not initialized, that is, the Son static block is not executed! See? Let’s break it down…

StrFather = Son strFather; strFather = Son strFather; For static fields, only the class that directly defines the field is initialized (executes a static block of code). This is especially true in inheritance, polymorphism!

Println (son.strfather); println(son.strfather); println(son.strfather); This code initializes the Father class instead of subclassing Son! Is that all at once clear? If you understand, please support the blogger to click a thumbs-up, thank you ~

Let’s look at problem number three from the beginning

package com.jvm.classloader;

class YeYe{
    static {
        System.out.println("YeYe static code block"); }}class Father extends YeYe{
    public final static String strFather="HelloJVM_Father";

    static{
        System.out.println("Father static code block"); }}class Son extends Father{
    public static String strSon="HelloJVM_Son";

    static{
        System.out.println(Son static code block);
    }
}

public class InitiativeUse {
    public static void main(String[] args) { System.out.println(Son.strFather); }} HelloJVM_FatherCopy the code

Final static! Yes, the variable corresponding to son.strfather is final static, and is still included in the second active use category of classes in this article: A static variable that accesses or assigns a value to a class or interface (except for static fields that are modified by final or, more accurately, where the compiler puts the result into the constant pool)

So, this problem does not initialize any classes, except the class on which the Main method is located! Println (son.strfather); StrFather: HelloJVM_Father: HelloJVM_Father: strFather: HelloJVM_Father: If you understand, please support the blogger and click a thumbs-up, thank you ~

In fact, the above topic does not fully explain the second point of the inductive active use category of the class in this article! How can I say that? How do you understand that? Let’s do one more procedure to make it clear to you

package com.jvm.classloader;

import sun.applet.Main;

import java.util.Random;
import java.util.UUID;

class Test{
    static {
        System.out.println("Static code block");
    }

// public static final String str= UUID.randomUUID().toString();
    public static final double str=Math.random();  // Compile time is uncertain
}


public class FinalUUidTest {
    public static void main(String[] args) { System.out.println(Test.str); }}Copy the code

Imagine the result. Would the contents of a static code block be executed?

Here we go. Here we go. Here we go

The results

staticStatic code block0.7338688977344875
Copy the code

This program fully illustrates the second point in the active use category of classes in this article: all fields that are modified by final are, in fact, static fields where the compiler places the result into a constant pool.

The compiler puts the result into the constant pool. When a constant’s value is not determined at compile time, the value is not placed in the constant pool of the calling class, which results in the active use of the constant class at runtime, so the class is initialized ==

So far, can understand the above three questions is already very good, but in order to better learn Java, the blogger has to give you a feast of burning brain, not ambitious, just want to top your knowledge of Java code, of course, also hope big guy gently hahaha, directly on the code:

package com.jvm.classloader;

public class ClassAndObjectLnitialize {

        public static void main(String[] args) {
            System.out.println("Output print statement");
        }

      public ClassAndObjectLnitialize(){

            System.out.println("Construction method");
            System.out.println("I am a child and my IQ =" + ZhiShang +"Emotional intelligence =" + QingShang);
        }

        {
            System.out.println("Common code block");
        }

        int ZhiShang = 250;
        static int QingShang = Awesome!;
        
        static
        {
            System.out.println("Static code block"); }}Copy the code

Suggest this problem do not spend too much time thinking, otherwise see the results you will find yourself thinking too much, resulting in the end you may see the results to hit the computer ha ha ha

Isolation operation results professional walk-on…

Isolation operation results professional walk-on…

Isolation operation results professional walk-on…

Isolation operation results professional walk-on…

Isolation operation results professional walk-on…

Run a print statement for the output of the resulting static code blockCopy the code

So, is it not as complicated as you think?

Classes that are marked as startup classes (JavaTest) and classes that use the Main method are initialized first

Uh huh? Classes with Main methods are supposed to be initialized. How come so many things are not implemented?

So what is the initialization order of a class? In our code, we only know that there is one constructor, but in fact, when Java code is compiled into bytecode, there is no constructor concept at first, just the == class initializer == and the == object initializer ==.

At this time we have to understand! So where do these two methods come from?

Class initializer methods: The compiler collects assignment statements, static code blocks, and class initializer methods in the order they appear. The == class initialization method is usually executed when the class is initialized. = =

So, in the example above, the class initializer will execute the following code:

 static int QingShang = Awesome!;  // Class variable (static variable) assignment statement

  static   // Static code block
   {
       System.out.println("Static code block");
   }
Copy the code

Instead of executing normal assignment statements and normal code blocks

Object initialization method, the compiler will appear in its order, collection: member variable assignment statements, common blocks of code, the last collection constructor code, the final component object initialization method, it is important to pay special attention to that if there is no monitoring or the constructor code, collection, will not be executed object initialization method. The == object initialization method is usually performed when the class object is instantiated. = =

In the example above, the object initialization method is this code:

    {                        
       System.out.println("Common code block");    // Plain code block
    }
 
    int ZhiShang = 250;   // Member variable assignment statement
    
    System.out.println("Construction method");  // Finally collect the constructor code
    System.out.println("I am a child and my IQ =" + ZhiShang +"Emotional intelligence =" + QingShang);
Copy the code

After understanding the class initialization method and object initialization method, let’s look at the above example! Yes! As mentioned above: if the constructor code is not monitored or collected, the object initialization method will not be executed. The above example does not perform object initialization. Forget? We didn’t instantiate class ClassAndObjectLnitialize at all! I’m just writing an output statement.

If we instantiate and verify, the code looks like this:

package com.jvm.classloader;

public class ClassAndObjectLnitialize {

        public static void main(String[] args) {
            new ClassAndObjectLnitialize();
            System.out.println("Output print statement");
        }

      public ClassAndObjectLnitialize(){

            System.out.println("Construction method");
             System.out.println("I am a child and my IQ =" + ZhiShang +"Emotional intelligence =" + QingShang);
        }

        {
            System.out.println("Common code block");
        }

        int ZhiShang = 250;
        static int QingShang = Awesome!;
        
        static
        {
            System.out.println("Static code block"); }} Run result: static code block normal code block constructor I am a bear child my IQ =250, eq =Awesome!Print statements for outputCopy the code

Bloggers have to make a point here! Why did I use these interview questions as part of this article? Because there are certain ways to learn, you can imagine, if the blogger did not cover and analyze these interview questions, would you have the patience to read this? Little white boy shoes said there was… Okay, so even if there is, you know, a whole bunch of theories, just ask yourself, do you know everything? Little white bar shoes can… Well, even if you could, can you promise not to forget the theory for a month? The little white bar said it was ok… I shit an old Beijing cloth shoes over the head to you hit crooked (I this temper my day). Therefore, study with interest, “purpose”, “ambition”! I hope this will help you, even a little…

5. Understand first active use

I mentioned above that Java programs can use classes in two ways: active and passive. Class initialization usually occurs only when the first active use of a class is made. The first keyword is important, so I’ll cover it in a brief summary!

How do you understand that? Here’s the old rule:

package com.jvm.classloader;

class Father6{
    public static int a = 1;
    static {
        System.out.println("Parent Static code block"); }}class Son6{
    public static int b = 2;
    static {
        System.out.println("Subclass bear Child static code block");
    }
}

public class OverallTest {
    static {
        System.out.println("Main method static code block");
    }

    public static void main(String[] args) {
        Father6 father6;
        System.out.println("= = = = = =");

         father6=new Father6();
        System.out.println("= = = = = =");

        System.out.println(Father6.a);
        System.out.println("= = = = = ="); System.out.println(Son6.b); }}Copy the code

Consider the results

Result: Main method static code block ====== parent static code block ======1====== subclass bear child static code block2
Copy the code

Analysis: First, classes using the Main method are initialized first according to the sixth principle of active use. So we do the Main method static block first, Father6 Father6; Just stating that a reference does nothing when run to father6=new father6 (); Father6 (father6.a); system.out.println (father6.a); father6 (father6.a); Father6 =new father6 (); Println (son6.b); system.out.println (son6.b); system.out.println (son6.b); Also, it initializes only itself, not its parent class, because the parent class Father6 is no longer actively used for the first time! Get it? If you have any questions welcome to leave a message, absolutely the first time reply!

Class loaders

Oh, finally the classloader content! All of the class loading we’ve talked about before is a foreshadowing of the class loader, and all of the classes we’ve talked about before are loaded by the class loader, so you can see how important class loaders are. Because the above interview questions do not involve the knowledge of class loaders, so I will cover the knowledge of class loaders again here!

The Class loader is responsible for loading all classes and generates a java.lang.Class instance object for all classes loaded into memory. Once a class is loaded into the JVM, the same class is never loaded again. Just as an object has a unique identity, so does a class loaded into the JVM.

About unique identifiers:

In Java, a class is identified by its fully qualified class name (including package and class names);

 

But in the JVM, a class is uniquely identified by its fully qualified class name and its classloader.

The Class loader’s job is to read the binary byte stream of a Class based on its fully qualified name into the JVM and convert it into an instance of a java.lang.Class object corresponding to the target Class. Three types of loaders are provided in the virtual machine. Bootstrap class loader, Extension class loader, System class loader (also known as application class loader), as follows:

Start the classloader: The BootstrapClassLoader is used to load classes required by the JVM itself. The BootstrapClassLoader is implemented in C++ language and is part of the virtual machine itself. It is responsible for loading the classes stored in JDK\jre\lib(JDK stands for JDK installation directory, the same below). Or libraries in the path specified by the -xBootCLASspath parameter that are recognized by the virtual machine (such as Rt.jar, all Java). The first classes are loaded by the BootstrapClassLoader. Startup class loaders cannot be directly referenced by Java programs. == Sum up a sentence: Jar, sunrsasign. Jar, charsets. Jar, jCE. Jar, jsse.jar, plugin.jar, and the classes stored in JRE\classes. Object, Stirng, List… = =

Extended class loaders: The ExtensionClassLoader, implemented by sun.misc.Launcher$ExtClassLoader, is responsible for loading the JDK\jre\lib\ext directory, Or all class libraries (such as Javax) in the path specified by the java.ext.dirs system variable. The developer can use the extension class loader directly.

Application class loaders: The ApplicationClassLoader is implemented by sun.misc.Launcher$AppClassLoader, which loads classes specified by the user’s ClassPath. Developers can use the ApplicationClassLoader directly. If the application does not have its own custom class loader, this is generally the default class loader in the application. In summary: The application class loader loads the CLASSPATH variable and the classes specified in the path are the classes you wrote in your project

Thread-context classloaders: In addition to the three types listed above, a special type is thread-context classloaders. Similar to thread.currentThread ().getContextClassLoader() gets the Thread context classloader

In everyday Java application development, class loading is almost always performed by the three types of loaders mentioned above. If necessary, we can also customize class loaders, because the JVM’s own ClassLoader only knows how to load standard Java class files from the local file system. So if you write your own ClassLoader, you can do the following:

1. Automatically validate digital signatures before executing untrusted code.

Dynamically create custom build classes that meet the specific needs of users.

3. Retrieve Java classes from specific locations, such as databases and networks.

Note that the Java VIRTUAL machine loads the class file on demand. That is, when the class file is needed, the Java virtual machine loads the class file into memory to generate a class object. In addition, when loading a class file, the Java virtual machine uses the parent delegate mode to delegate requests to the parent class. It is a task delegation mode, more on that below!

Let’s look at a program:

package com.jvm.classloaderQi;

public class ClassloaderTest {
    public static void main(String[] args) {
        // Get the loader for ClassloaderTest
        ClassLoader classLoader= ClassloaderTest.class.getClassLoader(); 
        
        System.out.println(classLoader);
        System.out.println(classLoader.getParent()); // Get the parent class loader of ClassloaderTestSystem.out.println(classLoader.getParent().getParent()); }}Copy the code

Running results:

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
Copy the code

Bootstrap Loader is implemented in C++ (this is limited to Hotspot, which is the default virtual machine after JDK1.5). There are many other virtual machines implemented in the Java language), cannot find a certain way to return the parent Loader, so returns NULL. The $sign is what the inner class means.

7. About namespaces

I think it’s important to know about namespaces when we talk about class loaders. In fact, an essential prerequisite for class loaders is namespaces!

Namespace concepts:

Each class loader has its own namespace, which is made up of the classes loaded by that loader and all the parent loaders.

 

Special attention:

No two classes in the same namespace have the same full name (including the package name of the class).

In different namespaces, it is possible to have two classes with the same full name (including the package name of the class). A class loaded by the child loader can see the parent loader’s class, and a class loaded by the parent loader cannot see the child loader’s class

We already know that each class can only be loaded once, but it’s not accurate to say that. What is accurate? That brings us to the concept of namespaces! Each Class can only be loaded once in the same namespace. Conversely, a Class can be loaded multiple times in different namespaces, and Class objects loaded multiple times are independent of each other!

7.1. How to understand?

Of course, directly throw the concept of namespaces to everyone, if you have not touched, 100% can not understand the meaning of it, I dare to guarantee that a false loss of 1 million… So the blogger adduce to write an example to let everybody profound experience once! Of course, these examples involve some knowledge of the custom loader, it is recommended to have a certain understanding of the custom loader in the first look!

Examples must know the premises: If you create a project using idea or eclipse, you will find the corresponding class files in the classPath directory. These directories are loaded by the ApplicationClassLoader. I will then place the class files on the system desktop addresses specified by the custom loader, so they are loaded by the custom loader

7.2, preparation,

File1 and File2 will appear on the ClassPath. Note that in the following example, the class file in the ClassPath path is always present.

The Main method:

  • Create a custom loader classloader2, declare the desktop class file path, and load File1

  • Print the loader for File1

  • 3. NewInstance an instance of File1

Class File1:

  • 1.File1There is a line of code in the constructor of:new File2newThe sample code

Class File2:

  • 1, print,File2The loader

7.3. Test Code Scenario 1

Delete the class files in the File1 and File2 projects. Delete both class files in the File1 and File2 projects (only class files in the system desktop path exist)

Result: The loaders for File1 and File2 are custom loaders

7.4. Scenario 2 of test code

Delete only the class files in the File1 project

Result: The loader for File1 is a custom loader, while the loader for the File2 instance is the App application loader

7.5. Test Code Scenario 3

Delete only the class files in the File2 project

Result: The loaders of File1 are all APP loaders, and a NoClassDefFoundError exception is raised when the File2 instance is executed

When loading a class (File1) that calls the initialization code of another class (File2) or other class methods, the class will try to load the delegate from that class’s loader. If none of the classes fails to load, NoClassDefFoundError is raised.

Of course, this is not enough to understand namespaces and class loading mechanisms!

The File2 class changes as follows:

  • 1.File1There is a row in the constructor ofnew File2This hasn’t changed
  • 2, inFile2In the constructor, print (access)File1The class file

7.6. Scenario 4 of test code

Delete only the class file File1 in the project

Result: The loaders for File1 are custom loaders, while the loaders for the instance of File2 are App loaders. A NoClassDefFoundError exception is raised when the constructor of File2 prints (accesses) the class file of File1

Conclusion: The classes loaded by the parent loader (File2Class loaded by child loader cannot be seen (File1)

The File1 method changes as follows: 1. NewInstance creates an instance of File1 while executing a new File2 in the constructor of File1. 2

7.7. Scenario 5 of test code

Delete only the class files in the File1 project

The result: File1 loaders are custom loaders, while the loaders that execute to instances of File2 are App loaders, which are fine when running to the class file that prints File2 in the File1 constructor

Class loaded by child loader (File1) can see parent loader’s class (File2)

It is important to note, of course, that if two loaders are not directly or indirectly parent-child, their loading classes are not visible to each other.

Of course, the whole situation above is quite abstract, after all, there is no code, if you have any questions, welcome to leave a message, Yichun absolutely the first time reply!

8. JVM class loading mechanism

There are three main class loading mechanisms for the JVM.

Full responsibility: When a Class loader is responsible for loading a Class, other classes that that Class depends on and references are also loaded by the Class loader, unless it is shown to be loaded using another Class loader

Parent delegate: Let the parent class loader attempts to load the class and only if the parent class loader cannot load the only from their own classpath to try loading the class, popular speak be sons all he is a lazy pig, no matter can’t do yourself, even if can load at first, first for his own father, throw up one by one, until to start the class loader is the best parent, Only when the father can’t do it, there is no way to do it by the next subclass, until a subclass can do it, and then the subclass will directly return, strength pit dad!

Caching mechanism: The caching mechanism will ensure that all loaded classes are cached. When a program needs to use a Class, the Class loader first looks for the Class in the cache. Only when the cache does not exist, the system reads the binary data corresponding to the Class, converts it into a Class object, and stores it in the cache. Is that why you have to restart the JVM to make Class changes to take effect

9. Parental delegation model

The workflow of the parental delegation model is as follows: If a class loader received the request of the class loading, it won’t try to load the first class, but give parent to complete the request, in turn up, as a result, all of the class loading request should be passed to the top finally start the class loader, only when the parent loader in the scope of its search did not find the required class, is unable to complete the loading, The child loader will try to load the class itself. That is, strength pit dad!

Parent delegate mechanism:

1. When an AppClassLoader loads a class, it does not attempt to load the class itself. Instead, it delegates the request to the parent class loader, ExtClassLoader.

 

2. When ExtClassLoader loads a class, it does not attempt to load the class itself in the first place. Instead, it delegates the class loading request to BootStrapClassLoader.

 

3. If the BootStrapClassLoader fails to load (for example, the class cannot be found in $JAVA_HOME/jre/lib), the ExtClassLoader will be used to try loading.

 

4. If the ExtClassLoader also fails to load, AppClassLoader will be used to load the ExtClassLoader. If the AppClassLoader also fails to load, ClassNotFoundException will be reported.

Abstract class ClassLoader class
All classloaders inherit from classloaders (excluding the launcher ClassLoader)
The loadClass() method is implemented by the ClassLoader class itself, and the logic in this method is an implementation of the parent delegate pattern

public Class<? > loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false); } protected Class<? > loadClass(String name, boolean resolve)
      throws ClassNotFoundException
  {
      synchronized (getClassLoadingLock(name)) {
          // Find the class object in the cacheClass<? > c = findLoadedClass(name);if (c == null) {
              long t0 = System.nanoTime();
              try {
                  if(parent ! =null) {
                      // If not, delegate to the parent class loader
                      c = parent.loadClass(name, false);
                  } else {
                  // If there is no parent class, delegate it to the boot loaderc = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
                  // ClassNotFoundException thrown if class not found
                  // from the non-null parent class loader
              }

              if (c == null) {
                  // If still not found, then invoke findClass in order
                  // If none is found, use the custom implementation findClass to find and load
                  c = findClass(name);

                  // this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {// Whether parsing is required at load time
              resolveClass(c);
          }
          returnc; }}Copy the code

Since there is the parents delegation model, then it must have the meaning of existence, the main meaning is that Java classes together with its class loader has a kind of hierarchical relationships with priority, through the hierarchy to avoid class reloading, when father already loaded the class, there is no need this loaded once again. Second, for security reasons, defined types in the Java core API will not be arbitrarily replaced. Suppose that a class named java.lang.Integer is passed over the network through the parent delegate mode to the initiator class loader, and the initiator class loader finds the class with this name in the core Java API and finds that the class has been loaded. Instead of reloading java.lang.Integer passed by the network, integer.class is returned, which prevents the core API library from being tampered with.

The significance of the parental delegation model can be summarized as follows: 1. System classes prevent multiple copies of the same bytecode from appearing in memory; 2

10. ClassLoader source code analysis

The ClassLoader class is an abstract class. All classloaders inherit from the ClassLoader (not including the launcher ClassLoader), so it is very important to analyze the ClassLoader abstract class.

A brief summary of some of the concepts in the ClassLoader abstract class:

Binary name: the format is as follows

Pay special attention to array types:

Class objects for array classes are not created by the class loader, but are created automatically according to the needs of the Java runtime. The array class has the same classloader as its element type, getClassLoader(). If the element type is primitive, the array class has no classloader, that is, NULL, which is pure NULL, unlike the null returned by the root classloader.

Here, I will analyze several important methods in the ClassLoader abstract class.

10, 1. LoadClass

This method loads binary types with specified names (including package names). This method is not recommended to be overridden after JDK1.2, but users can call this method directly. The loadClass() method is implemented by the ClassLoader class itself. LoadClass (String name, Boolean resolve) loadClass(String name, Boolean resolve) loadClass(String name, Boolean resolve) loadClass(String name, Boolean resolve)

public Class<? > loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false); } protected Class<? > loadClass(String name, boolean resolve)
      throws ClassNotFoundException
  {
      synchronized (getClassLoadingLock(name)) {
          // Find the class object in the cacheClass<? > c = findLoadedClass(name);if (c == null) {
              long t0 = System.nanoTime();
              try {
                  if(parent ! =null) {
                      // If not, delegate to the parent class loader
                      c = parent.loadClass(name, false);
                  } else {
                  // If there is no parent class, delegate it to the boot loaderc = findBootstrapClassOrNull(name); }}catch (ClassNotFoundException e) {
                  // ClassNotFoundException thrown if class not found
                  // from the non-null parent class loader
              }

              if (c == null) {
                  // If still not found, then invoke findClass in order
                  // If none is found, use the custom implementation findClass to find and load
                  c = findClass(name);

                  // this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {// Whether parsing is required at load time
              resolveClass(c);
          }
          returnc; }}Copy the code

As the loadClass method shows, when a class load request comes in, it looks for the class object in the cache, returns it if it exists, passes it to the class’s parent loader if it doesn’t exist, passes it to the top start class loader if it doesn’t have a parent, and finally, if it doesn’t find one, The findClass() method is used to load it (more on findClass() later). If you do not want to redefine the rules for loading classes, and do not have complex logic, you just want to load your specified classes at runtime, then you can use this.getClass().getClassloder.loadClass (“className”). You can call the loadClass method of the ClassLoader directly to get the class object.

10, 2. FindClass

Before JDK1.2, it was common to inherit the ClassLoader class and rewrite the loadClass method to implement custom class loading classes. However, after JDK1.2, it is no longer recommended that users override the loadClass() method. FindClass () is called in the loadClass() method. If the parent loadClass() method fails to load, the findClass() method is called in the loadClass() method. It calls its own findClass() method to complete the class loading, ensuring that the custom classloader also conforms to the parent delegate pattern. Note that there is no specific code logic in the ClassLoader class that implements the findClass() method. Instead, it throws a ClassNotFoundException, It should also be noted that the findClass method is usually used with the defineClass method (more on that later). The findClass() method in the ClassLoader class is listed below:

// Throw an exception directlyprotected Class<? > findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
}
Copy the code

10, 3. DefineClass (byte[] b, int off, int len)

The defineClass() method is used to parse the byte stream into a Class object that the JVM can recognize (the logic for this method is already implemented in the ClassLoader). This method allows you to instantiate Class objects not only from a Class file, but also in other ways. The defineClass() method is usually used with the findClass() method. In general, when customizing Class loaders, The findClass() method of the ClassLoader is overridden directly and the loading rule is written. The bytecode of the Class to be loaded is converted into a stream. The Class object of the Class is generated by calling defineClass().

protected Class<? > findClass(String name) throws ClassNotFoundException {
	  // Get the class's byte array
      byte[] classData = getClassData(name);  
      if (classData == null) {
          throw new ClassNotFoundException();
      } else {
	      // Use defineClass to generate a class object
          return defineClass(name, classData, 0, classData.length); }}Copy the code

Note that if you call defineClass() directly to generate a Class object, the Class object is not parsed (or linked, because parsing is the last step of linking) and the parsing will have to wait for initialization.

10, 4. ResolveClass (Class <? >c)

Using this method, the Class object of the Class can be created and resolved at the same time. Earlier we said that the linking phase is mainly about verifying the bytecode, allocating memory and setting initial values for class variables and converting symbolic references in the bytecode file to direct references.

10, 5.ClassLoader summary

The above four methods are the most important ones in the ClassLoader class, and they are the ones we will probably use a lot. SercureClassLoader extends the ClassLoader to include several methods for using code sources (the location of the source and its certificate) and permission definition class validation (access to the source code). As mentioned earlier, ClassLoader is an abstract class, and many methods are empty and unimplemented, such as findClass() and findResource(). The URLClassLoader implementation Class provides concrete implementation for these methods, and adds the URLClassPath Class to assist in obtaining Class bytecode streams and other functions. When writing custom Class loaders, if there is no too complex requirements, you can directly inherit the URLClassLoader Class. This avoids having to write the findClass() method and how it fetches the bytecode stream itself, making custom classloader writing much simpler.

LoadClass calls the findClass method by default after checking the parent loader. The findClass method in the ClassLoader basically throws an exception.

FindClass finds the corresponding class file based on the binary name and returns the class object class <? >

DefineClass is a method that basically converts a byte array into a Class instance and throws three exceptions, but only threws because the other two are runtime exceptions.

The loadClass method is to load a class file with the specified name, and call findLoadedClass (String) to check whether the class has been loaded… If it’s already loaded it doesn’t load it and it just returns the first load so a class is only loaded once

Custom class loaders

By default, the JVM uses the parent delegate mechanism. Although the parent delegate mechanism is very secure, there are some situations in which we need our own way to load. For example, the application transmits Java class bytecode over the network. These bytecodes are encrypted and cannot be loaded by the system class loader, so a custom class loader is required to implement ==. Therefore, custom class loaders are also necessary.

Custom classloaders are generally inherited from classloaders, so we just need to override the findClass method. Custom loader midpoint: Override findClass to see the flow of custom classloader code directly below:

package com.yichun.classloader;
import java.io.*;

public class MyClassLoader extends ClassLoader {
    private Stringroot; protected Class<? > findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] loadClassData(String className) {
        String fileName = root + File.separatorChar
                + className.replace('. ', File.separatorChar) + ".class";
        try {
            InputStream ins = new FileInputStream(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = 0;
            while((length = ins.read(buffer)) ! =- 1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root;
    }

    public static void main(String[] args)  {

        MyClassLoader classLoader = new MyClassLoader();
        classLoader.setRoot("D:\\dirtemp"); Class<? > testClass =null;
        try {
            testClass = classLoader.loadClass("com.yichun.classloader.Demo1");
            Object object = testClass.newInstance();
            System.out.println(object.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch(IllegalAccessException e) { e.printStackTrace(); }}}Copy the code

The core of the custom class loader is the retrieval of the bytecode file, which needs to be decrypted in the class if it is encrypted bytecode. The above code is only a simple Demo, does not encrypt the class file, so the decryption process is omitted. Here are a few things to note:

1, pass the filename here need to be a class defining all name, namely com. Yichun. Test. The classloading. Test format, because defineClass method is according to the process of this format.

 

2. It is best not to overwrite the loadClass method, as this can easily break the parent delegate pattern.

 

3, this kind of Test class itself can be AppClassLoader class loading, so we can’t put the com/yichun/Test/classloading/Test. The class under the class path. Otherwise, the parent delegate mechanism will cause the class to be loaded by the AppClassLoader rather than by our custom class loader.

12. Three ways to load classes

Here, I believe you have a certain understanding of class loading and loader, then do you know, in fact, there are three common ways to load a class, as follows:

Static loading, that is, using the new keyword to create instance objects.

 

2. Dynamic loading, that is, using the class.forname () method to load the object dynamically (the reflection load type) and then calling the Class’s newInstance() method to instantiate the object.

 

3. Dynamic loading, using the loadClass() method of the class loader to load the class, and then calling the newInstance() method of the class to instantiate the object

12, 1. Differences between the three ways:

1. The first and second methods use the same classloader, which is the current class loader. (this. GetClass. GetClassLoader). In 3, the user specifies the class loader.

2. If you need to find classes outside the current classpath, you can only use the third method. The class loaded in the third way belongs to a different namespace than the current class.

3. The first is static loading, while the second and third are dynamic loading.

12, 2. Two kinds of exceptions

A NoClassDefFoundError is raised if the class to be initialized is not found in the runtime during static loading, which is an Error in The JAVA exception system

2. If the class to be initialized is not found in the runtime during dynamic loading, a ClassNotFoundException is thrown, which is a Checked exception in JAVA

12, 3. Understand class.forname

Class.forname () is a static method of getting a Class object.

Class.forname () is a static method that can also be used to load classes. Class.forname () returns the Class object associated with the Class or interface given the string name. Notice that this is a way to get a Class object

The official API documentation is as follows

publicstatic Class<? > forName(String className) Returns the Class object associated withthe class or interface with the given string name. Invokingthis method is equivalent to: Class.forName(className,true, currentLoader) where currentLoader denotes the definingclass loader of the current class. For example, thefollowing code fragment returns the runtime Class descriptor for theclass named java.lang.Thread: Class t =Class.forName("java.lang.Thread") A call to forName("X") causes theclass named X to beinitialized. Parameters: className - the fully qualifiedname of the desired class. Returns: the Class object for the classwith the specified name.Copy the code

Class.forname (className) actually calls class.forname (className,true, this.getClass().getClassLoader()). The second argument is whether the Class must be initialized after loading. As you can see, the Class is initialized when it is loaded with class.forname (className). So the class.forname () method can be simply interpreted as: get the Class specified in the string argument and initialize that Class.

12, 4.Class.forName differs from classloader.loadClass

First, we must clarify the three processes of class loading: load > connect > initialize.

  • Class.forname () : In addition to loading the Class’s.class file into the JVM, the Class is interpreted, executing the static block of the Class;

  • Classloader.loadclass () : does only one thing: loads a. Class file into the JVM. Static blocks are executed only at newInstance.

  • Class.forname (name, initialize, loader) : Functions with parameters can also control whether static blocks are loaded. And only the newInstance() method is invoked to create objects of the class using the call constructor.

At this point, let’s look at another program:

package com.jvm.classloader;

class Demo{
    static {
        System.out.println("Static code block");
    }
}

public class ClassLoaderDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader=ClassLoaderDemo.class.getClassLoader();
        //1. Use classloader.loadClass () to load classes without executing initialization blocks
        classLoader.loadClass("com.jvm.classloader.Demo");
        
        //2. Use class.forname () to load classes. By default, initialization blocks are executed
        Class.forName("com.jvm.classloader.Demo");
        
        //3, use class.forname () to load the Class, specify the ClassLoader, no static block initialization
        Class.forName("com.jvm.classloader.Demo".false,classLoader); }}Copy the code

Remember one test at a time! My above program is written three at a time and labeled 1, 2, 3!! You run again from the computer, the train of thought will be very clear!

13, summary

Class load, connect, and initialize:

1. Load: Find and load the binary data of the class into the Java VIRTUAL machine

 

2. Connection:

Preparation: Allocates memory for static variables of a class and initializes them to default values, but the class variable is not initialized to its true initial value until initialization (if it is a class variable modified by final, it will be initialized to the desired value). Parsing: Converting symbolic references in a class to direct references is the process of looking for symbolic references to classes, interfaces, fields, and methods in the constant pool of a type and replacing them with direct references

Initialize: Assign the correct initial value to the static variable of the class

 

There are five stages when a class is loaded from disk into memory: load, connect, initialize, use, and unload

Java programs can use classes in two ways

(1) Active use (2) passive use

All Java virtual machine implementations must initialize each class or interface when it is “first actively used” by a Java program. They actively use (1) to create an instance of a class and (2) to access the static variable getStatic (mnemonic) of a class or interface. Or assign putStatic (3) to the static method invokestatic (4) to reflect (class.forname (” com.test.test “)) (5) to initialize a subclass of the Class (7) dynamic language support starting with JDK1.7 (understand)

Passive use Except for the seven cases above, any other way of using a Java class is considered passive use of the class and does not result in class initialization

14. Pay special attention

Initialize the entry method. After entering the initialization phase of class loading, the JVM looks for the entire main method entry and initializes the entire class that the main method is in. When a class needs to be initialized, the class constructor () is initialized first, followed by the object constructor ().

Initializing class constructors: The JVM collects the assignment statements, static code blocks of class variables in sequence, and eventually the class constructor is executed by the JVM. Initializes the object constructor: The JVM collects the constructor from the assignment statements that collect the member variables, the ordinary code blocks, and finally the constructor, and assembles them into object constructors that are executed by the JVM. In particular, if the constructor code is not monitored or collected, the object initialization method will not be executed. Object initialization methods are typically performed when class objects are instantiated.

If an initialization of another class is encountered while initializing the class of the main method, the corresponding class is loaded first and returned when the load is complete. This loop repeats until the class of main is returned.

If this article helped you at all, please give it a thumbs up. Thanks

Finally, if there is insufficient or improper place, welcome to criticize, grateful! If you have any questions welcome to leave a message, absolutely the first time reply!

Welcome everyone to pay attention to my public number, discuss technology together, yearning for technology, the pursuit of technology, good come is a friend oh…