Performance optimization|Interpret the interview questions, thoroughly understand the class loading and initialization sequence

Interpret the interview questions and thoroughly understand the class loading and initialization sequence

In the advanced interview process, I can't escape the interviewer's three consecutive questions:

  • Do you know how jvm loads classes?
  • Do you know the order of class initialization?
  • I have an interview question, can you answer it?

After three consecutive questions, I am afraid that I have been dismissed. Some students must have learned how to load classes in jvm, but when the interviewer asked them, I was confused. The main reason was that I did not grasp the essence and did not know it. Rot memorization is not enough, the interviewer will see it. This article will take everyone to analyze the interview questions and sort out the main points of knowledge. I believe that after reading it, you will definitely gain something. You can say goodbye to the interviewer's repeated questions.

Interview Question 1

Now we enter the main topic: the
interviewer directly throws over the first interview question to see if everyone can guess the result.

package org.apache.dubbo.demo.provider;

public class JVMClass extends BaseCodeBlock {
    {
        System.out.println("子类的普通代码块");
    }
    public JVMClass() {
        System.out.println("子类的构造方法");
    }
    @Override
    public void msg() {
        System.out.println("子类的普通方法");
    }

    public static void msg2() {
        System.out.println("子类的静态方法");
    }

    static {
        System.out.println("子类的静态代码块");
    }

    public static void main(String[] args) {
        BaseCodeBlock bcb = new JVMClass();
        bcb.msg();
    }
    Other o = new Other();
}

class BaseCodeBlock {

    public BaseCodeBlock() {
        System.out.println("父类的构造方法");
    }

    public void msg() {
        System.out.println("父类的普通方法");
    }

    public static void msg2() {
        System.out.println("父类的静态方法");
    }

    static {
        System.out.println("父类的静态代码块");
    }

    Other2 o2 = new Other2();

    {
        System.out.println("父类的普通代码块");
    }
}

class Other {
    Other() {
        System.out.println("初始化子类的属性值");
    }
}

class Other2 {
    Other2() {
        System.out.println("初始化父类的属性值");
    }
}

You can copy the above code to the editor and execute it to see if the result is consistent with your expected result. This code basically shows the class loading and initialization sequence, and you
Insert picture description here
can see the result if there is inheritance. For the parent class, the parent class will be initialized first. Follow this order

Insert picture description here
The static code block of the parent class -> the static code block of the child class -> the attribute value of the parent class is initialized / the ordinary code block of the parent class (executed in the order of the code) -> the construction method of the parent class -> the initialization of the child class Attribute value/subclass ordinary code block (arranged and executed in the order of code) -> subclass construction method.
The construction method is executed last.

Interview Question 2

The interviewer began to ask the second question, and then threw a piece of code, detailed product:

package com.example.demo;

public class JVMClass2 {
    public static void main(String[] args) {
        Singleton1 s1 = Singleton1.getSingleton();
        Singleton2 s2 = Singleton2.getSingleton();
        System.out.println("s1:counter1 = "+ s1.counter1);
        System.out.println("s1:counter2 = "+s1.counter2);
        System.out.println("s2:counter1 = "+ s2.counter1);
        System.out.println("s2:counter2 = "+s2.counter2);
    }
}
class Singleton1{
    private static Singleton1 singleton = new Singleton1();
    public static int counter1;
    public static int counter2 = 0;
    public Singleton1(){
        counter1++;
        counter2++;
    }

    public static Singleton1 getSingleton(){
        return singleton;
    }
}
class Singleton2{
    public static int counter1;
    public static int counter2 = 0;
    private static Singleton2 singleton = new Singleton2();
    public Singleton2(){
        counter1++;
        counter2++;
    }

    public static Singleton2 getSingleton(){
        return singleton;
    }
}

You can use your mind and combine the loading sequence above to analyze the answer to this question.
Let's look at the results directly
Insert picture description here

Is it a bit different from what we expected? Let’s analyze it according to the above thoughts.

  1. The constructor is executed after all initializations
  2. Then after all the attributes are initialized, when the constructor is executed, counter1 and counter2 will have the default initial value of 0, then after the constructor is executed, the results of counter1 and counter2 must be 1.
  3. If this is the case, the execution results of Singleton2 and Singleton1 should be the same, so why is Singleton2 consistent with our expectations, but Singleton1 is different?
  4. We see that the only difference between the two of them is the location where new is instantiated.
  5. Now that we have found the difference, we continue to analyze, we mainly analyze Singleton1 that is inconsistent with our expectations.
  6. Singleton1 is instantiated in the position of static variables. All of them are executed first, so entering the constructor execution, students may have questions. Didn't the above say that the constructor is executed at the end? Why is it executed first? Because the execution of this constructor is triggered by the instantiation code, the constructor will be executed internally first
  7. Enter the construction method. At this time, counter1 and counter2 still maintain zero values ​​(this is executed during the virtual machine loading class preparation phase, which will be described later). After executing ++, counter1 and counter2 will both become 1. The execution of the construction method ends, and enter the next step
  8. Counter1 is not displayed and assigned, counter2 is re-assigned to 0, and the initialization ends.
  9. Get counter1 and counter2 as 1 and 0 respectively

At this point, the interview has ended, we will now combine theoretical knowledge to summarize the JVM class loading and initialization sequence.

Java class loading is divided into five processes, as shown in the figure:

Insert picture description here

Class loading order

load

This stage is mainly to load the class file into the JVM. The class file can be from the local or a binary stream: JVM mainly does the following: 1) Obtain the XXX.class file through the classloader and read it as a binary stream Into memory. 2) Convert the static storage structure represented by the byte stream into the runtime data structure of the method area; 3) Generate a java.lang.Class object of this class in the memory as the access to various data of this class in the method area Entrance.

verification

This stage is mainly to ensure that the loaded class file can meet the JVM specification, especially the binary byte stream from the network, which must be verified to prevent unpredictable errors. Of course, if you are sure that your class file is too many If there is no problem in this test, you can use the parameter to close the verification, and those who are interested can understand. At this stage, the following verifications were carried out:
  1. File format verification:

    verify that the file format is in accordance with the specifications of the virtual machine, that is, the content in our previous class file structure, such as whether this is a Class file (see the magic number, whether it is CAFEBABE);

    Does the Java version meet the scope of the current virtual machine (Java can be downward compatible, but cannot handle programs larger than the current version), etc.

  1. Metadata verification: verify

    the metadata in the Class file, whether there is metadata information that does not conform to Java semantics. Some friends here may be more confused, what is metadata? Generally, there are data and metadata in a file. Data refers to the actual data, and metadata (Metadata) is the data used to describe the data. Friends who have used Java annotations should be familiar with the term metadata. The corresponding meta-annotations actually mean the same thing.

    For example: for example, we define a variable int a = 1; it can be understood that the data is 1, and the metadata is to describe a string variable "a", the type of this "a" is int type, its The value is also an int type 1, which is the data describing the data, which is the metadata.

  1. Bytecode verification:

    through data flow and control flow analysis, to determine whether the program semantics are legal.

    In terms of data, it is necessary to ensure that the type conversion is effective; for the code of the control flow, the instruction cannot be jumped to the bytecode instruction of other methods, etc...

  1. Verification of symbol references:

    In order to ensure that the parsing action can be completed normally, it is also necessary to determine whether other classes to be referenced comply with the regulations when the virtual machine converts the symbol references into direct references. For example, whether the class to be referenced can be found; whether the referenced attribute exists in the corresponding class, whether the permissions meet the requirements (private ones cannot be accessed), etc.

ready

The preparation phase is used to allocate memory for static variables in the method area and set the zero value. As mentioned above, it can be understood in series.

Parsing

The symbol reference in the constant pool of the virtual machine is resolved into a direct reference, pointing to a specific address in the memory.

initialization

At this step, we really start to execute our code, perform variable assignment and corresponding initialization operations. If there is an inheritance relationship, the parent class will be initialized first. ### use is to use. . .

Uninstall

JVM will mark the object as null and wait for the garbage collector to GC

After understanding the five major steps of loading, let's look at the last important knowledge point, which is when we use the class, when will the initialization of the class be triggered, divided into the following four situations:

When will the class be initialized

  1. When encountering the four direct code instructions new, getstatic, putstatic or invokestatic, such as a new class, reading a static field (not final modified), or calling a static method of a class.
  2. When using the java.lang.reflect package method to make a reflection call to a class, if the class is not initialized, its initialization needs to be triggered.
  3. Initialize a class, if its parent class has not yet been initialized, the initialization of the parent class is triggered first.
  4. When the virtual machine starts, the user needs to define a main class to be executed (the class containing the main method), and the virtual machine initializes this class first.
  5. When using the dynamic dynamic language of JDK1.7, if the final analysis structure of a MethodHandle instance is the method handle of REF_getStatic, REF_putStatic, REF_invokeStatic, and the handle is not initialized, you need to trigger the initialization first.

This is the end of the interview content shared today. See you in the next issue. A video tutorial and interview materials are attached at the end of the article.

Insert picture description here
Insert picture description here

A search on WeChat [Le Zai open talk] Follow the handsome me, reply [Dry goods], there will be a lot of interview materials and architect must-read books waiting for you to choose, including java basics, java concurrency, microservices, middleware, etc. The information is waiting for you.

The more you read the book without thinking, you will feel that you know a lot; and the more you read and think, the more clearly you will see that you know very little. --Voltaire

Guess you like

Origin blog.csdn.net/weixin_34311210/article/details/108682164