How the JVM works

Java code files are compiled into.class bytecode files, the class loader loads the classes from the.class bytecode files into the JVM, and the JVM executes the code. The following figure shows the execution process

Class loader

Class loading process

Load -> Verify -> Prepare -> Parse -> Initialize -> Use -> Uninstall

loading

Once the JVM process is started, the class must be loaded into memory and executed from the entry code of the main() method

public class HelloWorld {
    public static void main(String[] args) {}}Copy the code
  • Loading means reading the CLass file into memory and creating a java.lang.class object
  • Class loaders are done by class loaders, which are typically provided by the JVM, in addition to creating your own class loaders by inheriting the ClassLoader base class
  • By using different class loaders, you can load the binary data of a class from different sources
    • Load a class file from the local file system
    • Load the class file from the JAR package, which contains the database driver classes used for JDBC programming, and the JVM can load the class file directly from the JAR file
    • Load class files over the network
    • Dynamically compile and load a Java source file

Preparation stage

validation

Verify that the contents of a loaded “. Class “file conform to the specified specification against the Java Virtual Machine specification. If the”. Class “file is tampered with and the bytecodes inside it do not conform to the specification, then the JVM cannot execute the bytecodes. After loading the “.class “file into memory, you must verify that it conforms to the JVM’s specifications before running it. It mainly includes four kinds of verification, file format verification, metadata verification, bytecode verification, symbol reference verification

  • File format verification: Verifies that the byte stream complies with the Class file format specification and can be loaded by the current VIRTUAL machine. For example, check whether the major and minor versions are in the range processed by the current VM. Are there any unsupported constant types in the constant pool? An index value in a pointer to a constant that does not exist or does not conform to a type

  • Metadata verification: Semantic analysis of the information described by bytecode to analyze whether it conforms to the specification of Java language syntax

  • Bytecode validation: The most important validation step, analyzing the data flow and control to determine that the semantics are legitimate and logical. The main purpose is to verify the method body after metadata verification. Ensure that class methods do not have hazards at runtime

  • Symbol reference verification: mainly for the conversion of symbol reference to direct reference, it will extend to the third parsing stage, mainly to determine the access type and other situations involving references, mainly to ensure that the reference will be accessed, there will be no class inaccessible problems

To prepare

Allocate some memory for the referenced class, and then allocate memory for the class variable inside (that is, static modified variable), with a default initial value

parsing

This is actually the process of replacing symbolic references in the binary data of a class with direct references. This part of the process is more complex and is designed down to the bottom of the JVM

  • Symbolic reference: A symbolic reference is a set of symbols that describe the referenced object. The symbol can be any literal literal, as long as it can be located without conflict. Layout is independent of memory
  • Direct reference: a pointer to a target, an offset, or a handle that can be directly located. The reference is related to the layout in memory and must be loaded in

Analysis mainly includes:

  • Class or interface resolution
  • Field analytical
  • Class method resolution
  • Interface method parsing

Initialize the

Class initialization assigns the correct initial value to the static variable of the class. The preparation phase and the initialization phase may seem contradictory, but they are not contradictory. Private static int a = 10 private static int a = 10 private static int a = 10 Private static int A = 10 private static int A = 10 That is, a=0, and then at the parsing step, at the initialization step, the true value of A, 10, is assigned to A, where a=10

A very important rule is that if a class is initialized and its parent is not initialized, it must initialize its parent first

loader

Start the class/root class loader

Bootstrap ClassLoader, which is responsible for loading the core classes in the Java directory we installed on the machine, is implemented using native code. Does not inherit from java.lang.ClassLoader (which is responsible for loading all classes in jre/lib/rt.jar in $JAVA_HOME, implemented by C++, not ClassLoader subclass). Since bootstrap classloaders involve the details of the virtual machine’s local implementation, the developer cannot get a reference to the bootstrap classloader directly, so direct operations are not allowed

Extend the class loader

Extension ClassLoader, which is responsible for loading the JRE Extension directory, lib/ext, or JAR package classes in the directory specified by the java.ext.dirs system property. Implemented by the Java language, the parent class loader is null

Application class loader

The Application ClassLoader, called the Application (or system) ClassLoader, is responsible for loading the -classpath option, java.class.path system properties from Java commands, at JVM startup. Or CLASSPATH replaces the JAR package and CLASSPATH specified by the variable. A program can obtain the system ClassLoader through the static method getSystemClassLoader() of the ClassLoader. If not specified, user-defined class loaders use this class loader as the parent loader. Implemented by Java language, the parent class loader is ExtClassLoader.

Class loaders go through the following eight steps to load a Class:

  • Check if the Class is loaded, that is, if it is present in the buffer, if so go to step 8, otherwise go to Step 2

  • If there is no Parent classloader, then either Parent is the root classloader or is itself the root classloader, and skip to step 4, or step 3 if the Parent classloader exists

  • Request to use the parent class loader to load the target class, if successful skip to step 8, otherwise proceed to step 5

  • Request to load the target class using the root class loader, skip to step 8 if successful, skip to step 7 otherwise

  • The current Class loader attempts to find the Class file and performs step 6 if it finds it, or step 7 if it does not

  • Load the Class from the file and skip to step 8

  • Throw ClassNotFountException

  • Returns the corresponding java.lang.Class object

Custom class loaders

In addition to the above, you can also customize class loaders to load your classes according to your own needs

Class loading mechanism

There are three main JVM class loading mechanisms:

  • Full responsibility: When a Class loader is responsible for loading a Class, other classes that the 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: A parent delegate lets the parent Class loader try to load the Class, and only tries to load the Class from its own classpath if the parent Class loader fails to load the Class. Generally speaking, when a specific class loader receives a request to load a class, it first delegates the loading task to the parent loader, recursively. If the parent loader can complete the class loading task, it will return successfully. Only if the parent loader is unable to complete the load task, do it yourself
  • Caching mechanism. The caching mechanism will ensure that all loaded classes will be cached. When a program needs to use a Class, the Class loader first searches for the Class in the cache. Only when the Class object does not exist in the cache, the system will read the binary data corresponding to the Class, convert it into a Class object, and store it in the buffer. This is why the JVM must be restarted after a significant Class change is made for the program’s changes to take effect

Parent delegation mechanism

This is known as the parent delegate model: the parent asks the father to load it, and the son loads it if it doesn’t work

  • Parents delegate mechanism, its working principle is that if a class loader received class loading request, it will not go to loading, but delegates the request to the parent class loader to execution, if the parent class loader is its parent class loader, further upward, recursive in order, the request will eventually reach the top of start the class loader, If the parent class loader finish class loading can be successfully returns, if the parent class loader cannot complete the task load, child loader will try to load, it is the parents delegate pattern, namely every son is lazy, every time just throw it to the father to do, I also do until my father said it, his son to think of some way to to complete

  • Advantages of parent delegation: The advantage of using parent delegation is that Java classes have a hierarchy of priorities along with their classloaders. This hierarchy avoids reloading classes. There is no need for the child ClassLoader to load the class once the parent has already loaded the class. 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