Java bytecode
To execute a java file, you first need to compile the Java source code into a class file, that is, a bytecode file. Bytecode file is a platform independent binary file. The data content in bytecode needs to comply with the specification of JVM before it can be parsed correctly. For example, the beginning of bytecode file is a flag bit called magic number. All class files conforming to JVM specification need to start with magic number 0xcafe babe.
Java class loading mechanism
The JVM loads the bytecode file into memory and performs Initialization. This process is called class loading process. The class loading process is divided into three steps: loading, Linking and Initialization.
Loading phase
Don't confuse the loading phase with the class loading process. The loading phase is only a step of class loading, which mainly has the following characteristics:
-
Obtain the binary data of bytecode of the specified class through the full class name, and convert the static data structure of this class into the runtime data structure in the method area
-
Generate a Java. Java corresponding to this class in memory Lang.class file. Each class file will generate a class object as the class entry file of the method area
-
The main work of the loading phase is to load the binary file of the class into memory through the full class name, and create a Java Lang.class object
Linking phase
The Linking phase is also called the Linking phase. This phase is also divided into three steps: verifying verify, preparing and resolving. The main work is to verify the data represented by the class file, the class variables, and replace the symbolic references in the constant pool with direct references.
Verify: mainly for security reasons, the JVM needs to verify whether the incoming bytecode conforms to the JVM specification. Because the definition of JVM is a cross language platform, no matter what language, as long as the generated bytecode conforms to the JVM specification, it can be compiled and executed through the JVM. Moreover, the incoming bytecode can be from any source, such as file, network, database, etc. If the incoming bytecode content is not verified, there may be security risks or interruption of JVM operation. The verification contents mainly include file format verification, metadata verification, bytecode verification, symbol reference verification, etc.
Prepare: it is mainly used to allocate memory and set the default value for class variables. Class variables are variables that use the static modifier. The default value is the zero value of the variable type. For example, the zero value of int is 0, and the zero value of reference type is null. This process will not set the default value for the instance variable, and if a class variable is decorated with the final keyword at the same time, it is a constant value. The initialization of this constant value has been completed when compiling the generated class file.
Resolve: the main work of this step is to convert the symbolic reference in the constant pool into a direct reference. Generally, this process may not be executed until the JVM Initialization is completed, because there is a direct reference at that time. Direct reference refers to the address directly pointing to the object, while symbolic reference refers to only an identification. The parsing phase can be considered as the real allocation of a memory address to the object of the constant pool. This memory address can be really accessed. Instead of just a symbol.
Therefore, the main work in the Linking stage can be summarized as verifying the class file, setting the default value of class variables, and replacing the symbol reference in the constant pool with direct reference.
Initialization phase
In the Initialization phase and Initialization phase, the main work is to execute the class constructor method. This class constructor method does not need to be defined by the programmer, but the JVM automatically collects the assignment operations of class attributes defined in the class and the statements in the static code block. In this process, static can only reference and assign values to class variables defined in front of it. Class variables defined behind it can be assigned values, but they cannot be used. Otherwise, forward reference exceptions will be reported.
Java class loading mechanism
The JVM guarantees that the method of the parent class of the class will be executed before the method of the current class, so Java Lang.Object will be executed first because it is the parent of all classes.
The JVM will ensure that the class will be executed correctly in a multithreaded environment, and ensure that only one thread can initialize the class at the same time.
The main work of class initialization stage is to initialize class variables and call class initialization methods.
Class loader
Class loader is required to implement the class loading process. There are three built-in class loaders in Java, namely BootStrap ClassLoader, Extension Classloader and system cloassloader (or APP Classloader). In addition to these three types, users can also customize the class loader. The user-defined class loader only needs to inherit the ClassLoader class and override the findClass method or loadClass.
The hierarchy of the three built-in class loaders is as follows:
BootStrap ClassLoader <— Extension ClassLoader <— System ClassLoader
In essence, these three loaders are not inheritance, but a hierarchical relationship realized through combination. The general functions of each built-in loader are as follows:
BootStrap ClassLoader: the topmost class loader, which is mainly used to load Java's core class libraries, such as JAVA_HOME/jre/lib/rt.jar,resouces.jar, etc. The bottom layer is implemented through C/C + +, which is built into the JVM and does not inherit from the ClassLoader class. The other two built-in class loaders, Extension ClassLoader and System ClassLoader, are loaded through BootStrap ClassLoader. Formally, it is the parent class of the top level of other class loaders. For security reasons, the BootStrap loader only loads classes beginning with Java, javax, and sun, which will be explained under the parental delegation mechanism.
Extension ClassLoader: an extension class loader whose parent class loader is Bootstrap ClassLoader. The extension class loader is written through a java program and inherits from the ClassLoader class. It is mainly used to load JAVA_HOME/jre/ext directory or Java The class libraries in the directories specified by the ext.dris environment variable. If users place the class libraries written by themselves in these directories, they will also be loaded by the extension class loader.
System ClassLoader: system ClassLoader, also known as application ClassLoader. Its parent class loader is an extension class loader. The system class loader is written in Java and inherits from the ClassLoader class. It is the default class loader. Java applications are mostly loaded through it and can be used to load Java class. The class library under the directory represented by the path environment variable and the class library under the classpath environment variable.
public class User { public static void main(String[] args) { // Through classloader Getsystemclassloader can get // system class loader ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); // The String class is Java Under Lang package // So the class loader is BootStrap ClassLoader // It is implemented by native, so it cannot be obtained. null is returned System.out.println(String.class.getClassLoader()); // You can see that the class loader of the user-defined class is Launcher$AppClassLoader // That is, the Application Loader, and its parent class loader is Extension ClassLoader // The parent class loader of Extension ClassLoader is BootStrap ClassLoader // Because it is implemented in C/C + +, it cannot be obtained and is null ClassLoader classLoader = User.class.getClassLoader(); System.out.println(classLoader); System.out.println(classLoader.getParent()); System.out.println(classLoader.getParent().getParent()); } }
Parental delegation mechanism
When the class loader performs class loading, it does not immediately try to load the specified class, but delegates it to the parent class for loading. For example, when the system class loader loads, it will delegate the class loading to Extension ClassLoader, and Extension ClassLoader will delegate to BootStrap ClassLoader, because BootStrap ClassLoader is the top-level class loader, Therefore, it will not delegate upward again, but try to load. If it fails to load successfully, the subclass will try to load again. The same is true for the loading mechanism of custom class loaders. Of course, if all class loaders fail to load successfully, ClassNotFound exception will be thrown.
The advantages of parental delegation mechanism mainly include:
For security reasons, the core class library of Java can be prevented from being tampered with, because Java programs can define their own packages and classes. If the user defines a class library starting with Java, javax or sun, it will not be loaded. Because all classes are loaded first through BootStrap ClassLoader, BootStrap ClassLoader will ensure whether it is within its loading range and whether it can be loaded correctly. Generally, if user-defined class libraries starting with javja, javax and sun appear, the following errors will be reported.
Java class loading mechanism
Prevent the class from being loaded many times. As long as the class is loaded, it will be cached. Therefore, there will be no multiple loading.
Custom class loader
It is mainly realized by inheriting ClassLoader class and overriding findClass method.
package com.example.demo; import lombok.SneakyThrows; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; public class CustomClassLoader extends ClassLoader { private String path; public CustomClassLoader(String path){ this.path = path; } @SneakyThrows @Override protected Class<?> findClass(String className) { Class clz = null; byte[] data = getClassData(); if (data != null){ clz = super.defineClass(className, data, 0, data.length); } return clz; } public byte[] getClassData() throws IOException { FileInputStream inputStream = new FileInputStream(new File(path)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int len = 0; while ((len = inputStream.read(data)) != -1){ outputStream.write(data, 0, len); } byte[] result = outputStream.toByteArray(); outputStream.close(); inputStream.close(); return result; } public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { String path = "H:\\code\\java\\demo2\\target\\classes\\com\\example\\demo\\bean\\User.class"; CustomClassLoader classLoader = new CustomClassLoader(path); Class user = classLoader.loadClass("com.example.demo.bean.User"); System.out.println(user); Object ins = user.newInstance(); System.out.println(user.getDeclaredMethod("test", null).invoke(ins, null)); } } package com.example.demo.bean; public class User { public String test(){ return "Test ClassLoader"; } }
Summary:
-
Class loads the JVM bytecode, which can come from any media, as long as it conforms to the JVM specification.
-
The general process of class loading mainly includes three stages: loading - > LINGKING - > Initialization.
3. Class loading is realized through class loader. The built-in class loader includes BootStrap ClassLoader, Extension ClassLoader and System ClassLoader.
4. Class loading adopts the two parent delegation mechanism, which can avoid repeated loading and overwriting the loading of java core library.
5. The custom class loader can override the findClass method by inheriting the ClassLoader class.
6. In the whole class loading process, only the loading stage can be controlled by the programmer, and other stages are controlled and dominated by the JVM.