Please go to DobbyKim’s Question of the Day for more questions

A:

When the JVM encounters a new instruction, it first checks to see if the instruction’s parameters can be located in the constant pool to a Symbolic Reference of a class. It also checks to see if the class represented by the Symbolic Reference has been loaded, parsed, and initialized, that is, to verify whether the class has been used for the first time. If the class is being used for the first time, the class loading process is performed.

Note: A symbolic reference is a reference to a class where another class is introduced, but the JVM does not know where the other classes are introduced, so it uses a unique symbol instead. When the class loader parses the symbol reference, it uses the symbolic reference to find the address of the reference class

Step 1: Class loading

The entire life cycle of a class from when it is loaded into the JVM to when it is unloaded out of memory is shown here:

Load -> Connect (Verify -> Prepare -> Parse) -> Initialize -> Use -> uninstall

The main functions of each stage are:

  • Load: Finds and loads the binary data of the class file

  • Connect: Merging the binary data of classes that have been read into memory into the JVM runtime environment involves the following steps:

    • Validation: To ensure the correctness of the classes being loaded

    • Preparation: Allocates memory for static variables of the class, assigning default values; Public static int a = 1; Assign a default value of 0 to the static variable a in the preparation phase

    • Parse: Convert symbolic references in the constant pool to direct references

  • Initialization: Assigns an initial value to a static variable of the class. This is when the static variable a is initialized with the value 1

As you can see, the static members of the class are already loaded into memory during class loading!

So how do classes get loaded? The answer is: class loaders

Class loader

Java vm loaders include the following :(start in JDK9)

  • Start the class loader (BootstrapClassLoader)

  • PlatformClassLoader

  • Application class loader (AppClassLoader)

The loader provided by the JDK8 VM:

  • BootstrapClassLoader

  • ExtensionClassLoader

  • AppClassLoader

In addition to the class loaders provided by VMS, users can also customize class loaders.

Hierarchical relationships between class loaders:

  • The parent of UserClassLoader (user-defined class loader) is AppClassLoader

  • The parent of AppClassLoader is PlatformClassLoader

  • The parent of PlatformClassLoader is BootstrapClassLoader

The diagram is as follows:

Parental delegation model

The ClassLoader in the JVM loads a class in the parent-delegate model:

So what is the parental delegation model?

The parental delegation model is: If a ClassLoader receives a classload request, it first does not attempt to load the class itself. Instead, it delegates the request to the parent ClassLoader. This is true at every level of classloaders, so all load requests should eventually be sent to the top level of the launcher. Only when the parent class loader reports that it cannot complete the load request (it did not find the required class in its search scope) will the child loader attempt to load it itself.

The advantage of using the parent delegate mechanism is that the global uniqueness of a class is effectively guaranteed. When multiple classes with the same qualified name appear in the program, the class loader will always load only one of them.

After class loading is complete, the JVM can fully determine the memory size of the new object, and then the JVM performs the task of allocating memory for that object.

Step 2: Allocate memory for the object

The task of allocating space for an object is equivalent to dividing a certain size of memory from the JVM heap, and there are two common ways to do this (using different allocation mechanisms depending on the garbage collector being used) :

  • Bump the Pointer
  • Free List
Pointer to the collision

A pointer collision is when, assuming that the JVM heap is perfectly clean, all used memory is placed on one side, free memory is placed on the other, and a pointer in the middle points to the split point. The new object allocates memory by moving that pointer to the free space by an equal amount of the object’s size.

The free list

If the JVM heap memory is not tidy, that is, used memory is interspersed with free memory, the JVM maintains a free list of memory blocks that are available, and when allocating space for the object, the JVM finds a chunk of the free list that is large enough for the object to use.

Step 3: Refine the information about the object’s memory layout

After we allocate memory for the object, the JVM sets up some information about the object’s memory layout.

The layout of objects stored in memory (in the case of the HotSpot VIRTUAL machine) is divided into object headers, instance data, and aligned padding.

  • Object header The object header contains two parts:
    • Mark Word: Stores the run data of the object itself, such as Hash Code,GC generation age, lock status flags, etc
    • Type pointer: A pointer to the metadata of an object’s class
  • The instance data
    • Instance data is where the actual instance of an object is stored
  • Alignment filling
    • It doesn’t have to exist, it doesn’t have any special meaning, it’s just a placeholder. Because HotSpot requires that the object start address be a multiple of 8 bytes, if not aligned

The JVM assigns zero values to all instance data (default values), that is, copies the definition of instance variables in the method area to the heap, and assigns default values, such as 0 for integers, NULL for reference types, and so on.

In addition, the JVM makes the necessary Settings for the object header, such as which class the object is an instance of, how to find the metadata information for the class, the Hash Code of the object, the GC age of the object, and so on, all stored in the object header.

Step 4: Call the instantiation method of the object<init>

After the JVM has completed information about the memory layout of the object, the

method of the object is called to assign values to the variables of the object based on the attribute values passed in.

We described the class loading process above (load -> connect -> initialize), where the JVM displays assignments for static variables of the class and executes static code blocks. This step is actually done by the JVM-generated

method.

The order of execution of < Clinit > is:

  1. Parent static variable initialization
  2. Superclass static code block
  3. Subclass static variable initialization
  4. Subclass static code block

When we create an instance of a new object, we call the object class constructor to initialize it, which executes the

method.


is executed in the following order:

  1. Parent class variable initialization
  2. Parent ordinary code block
  3. Superclass constructor
  4. Class variable initialization
  5. Subclass plain code blocks
  6. Subclass constructor

About the

method:

  1. There are as many constructors as there are constructors<init>methods
  2. <init>This includes non-static variable assignment, non-static code block execution, and constructor code
  3. Non-static code assignment operations and non-static code blocks are executed from top to bottom, with the constructor executing last

About the differences between < Clinit > and

methods:

  1. <clinit>Method is executed during the class load initialization step,<init>Executed during instance initialization
  2. <clinit>Perform static variable assignment and execute static code block, while<init>Performs assignment of non-static variables and executes non-static code blocks and constructors
Step 5: Create a reference to the object in the stack and point to the new object instance in the heap

There is nothing to explain here, we operate on an object by manipulating a reference to the stack.

Extension: Access location of objects

After describing a new object, let’s take a quick look at how to access it.

The JVM specification only stipulates that the reference type is a reference to an object, but it does not specify how to locate and access the object in the heap. Therefore, the object access depends on the specific implementation of JVM. At present, there are two main ways to access the object: indirect access by handle and direct access by pointer.

handle

The address of the handle is stored in reference, and the address of the handle is stored in the instance data of the object and the address of the metadata:

Direct Pointers

The JVM heap stores the address of the metadata that accesses the class. Reference stores the address of the object instance:

Accessing objects by handle is an indirect reference (twice reference) method for accessing objects in heap memory, which has the disadvantage of running slightly slower; Using a pointer is faster because it reduces the overhead of a pointer location, so the current mainstream JVM, HotSpot, uses a direct pointer approach.