This article is too hardcore and is recommended for those with Java experience.
1. What is class loading?
Loading a class involves reading binary data from the class’s.class file into memory, placing it in the method area of the runtime data area, and then creating a java.lang. class object in the heap that encapsulates the class’s data structure in the 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.
Class loaders do not need to wait for “first active use” of a class to load it. The JVM specification allows classloaders to preload a class when they expect it to be used. If they encounter missing.class files or errors during preloading, 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.
How to load. Class files - directly from the local system - Download. Class files from the network - Load. Class files from zip, JAR archives - Extract. Class files from a proprietary database - Dynamically compile Java source files into. Class filesCopy the code
2. Class lifecycle
When a type is loaded into vm memory and unloaded from memory, 2. Its life cycle must go through Loading, Verification, Preparation, Resolution, Initialization, Using and Unloading. The three parts of validation, preparation and parsing are collectively referred to as Linking.
Load, validation, preparation, initialization and unload the order of the five stages is certain, the type of loading process must, in accordance with the order, step by step while parsing stage does not necessarily: in some cases it can start again after the initialization phase, this is in order to support the Java language runtime binding features (also called dynamic binding or late binding).
2.1 Loading
Load-time Is the first phase of the class loading process, during which the virtual machine needs to do three things:
-
Gets the binary byte stream defined by the fully qualified name of a class.
-
Transform the static storage structure represented by this byte stream into the runtime data structure of the method area.
-
Generate a java.lang.Class object in memory that represents the Class and acts as an access point for the Class’s various data in 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.
2.2 Verification
Validation is the first step in the connection phase, where the purpose is to ensure that the information contained in the byte stream of a Class file complies with all constraints of the Java Virtual Machine Specification, and that the information can be run as code without compromising the virtual machine itself.
Validation is the first step in the connection phase, which ensures that the byte stream in the Class file meets the requirements of the current virtual machine and does not compromise the security of the virtual machine itself. 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
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. There are several points to note about this phase:
- Only class variables (static) are allocated, not instance variables, which are allocated in the Java heap along with the object at instantiation.
- The initial value set here is usually the default zero value of the data type (such as 0, 0L, NULL, false, etc.) rather than the value explicitly assigned in Java code.
2.4 Initialization
The initialization phase of a class is the last step in the class loading process. In addition to the user application’s partial participation in the loading phase through custom class loaders, the rest of the class loading actions are completely controlled by the Java VIRTUAL machine. It is not until the initialization phase that the Java virtual machine actually begins to execute the Java program code written in the class, handing control to the application.
There are two ways to initialize a class variable in Java:
- Declare class variables to specify initial values.
- Use static code blocks to specify initial values for class variables.
Class loaders
The Class loader is responsible for loading all the classes, loading them into memory, and generating an instance of java.lang.class. Once a class is loaded into the JVM, it is never loaded again.
- Bootstrap ClassLoader: It loads Java core classes, such as String and System classes.
- Extension ClassLoader: The Extension library that loads the JRE.
- System ClassLoader: This is responsible for loading the JAR packages and CLASSPATH specified by the CLASSPATH environment variable.
- User class loader: A user – defined loader that has the class loader as its parent.
A simple chestnut:
public static void main(String[] args) {
ClassLoader loader = ClassLoader.getSystemClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
Copy the code
Output result:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
Copy the code
Why is the root class loader NULL?
The Bootstrap Loader (Bootstrap Loader) is not implemented in Java, but is implemented in C language. It cannot find a certain way to return the parent Loader, so null is returned.
JVM class loading mechanism
- 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 loader try to load the class first, and only try to load the class from its own classpath if the parent loader is unable to load the class.
- The caching mechanism will ensure that all loaded classes will be 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 will read the binary data corresponding to the Class, convert it into a Class object, and store it in the cache. This is why after Class changes are made, the JVM must be restarted for the program changes to take effect.
4. 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.
Parent delegate mechanism:
- When an AppClassLoader loads a class, it first does not attempt to load the class itself. Instead, it delegates the classloading request to the parent class loader, ExtClassLoader.
- When ExtClassLoader loads a class, it doesn’t try to load the class itself in the first place. Instead, it delegates the class loading request to BootStrapClassLoader.
- 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.
- If the ExtClassLoader also fails to load, AppClassLoader is used to load it. If the AppClassLoader also fails to load, ClassNotFoundException is reported.
ClassLoader#loadClass JDK 1.8.0_221
protectedClass<? > loadClass(String name,boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First check if the type is already loadedClass<? > c = findLoadedClass(name);if (c == null) {
// If it is not loaded, delegate to the parent class loader or delegate to the start class loader
long t0 = System.nanoTime();
try {
if(parent ! =null) {
// Delegate to the parent class loader if there is one
c = parent.loadClass(name, false);
} else {
// If there is no parent Class loader, check whether the Class was loaded by the bootloader, by calling the native Class findBootstrapClass(String name).c = 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
// to find the class.
long t1 = System.nanoTime();
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) {
resolveClass(c);
}
returnc; }}Copy the code
The parent delegate model is to prevent multiple copies of the same bytecode from appearing in memory and ensure the stable operation of the program.
5. Custom class loaders
At the beginning, I’d like to introduce the scenarios where custom classloaders work:
- Encryption: Java code can be easily decompiled. If you need to encrypt the code to prevent decompilation, you can encrypt the compiled code with an encryption algorithm so that the encrypted class can no longer be loaded by Java ClassLoader. In this case, you need to customize the ClassLoader to decrypt the class before loading it.
- Load code from non-standard sources: If our bytecode is in a database, or even in the cloud, we can customize class loaders to load classes from specified sources.
For a quick example, first we create a target class that needs to be loaded:
public class ClassLoaderTest {
public void hello(a) {
System.out.println("I was made by." + getClass().getClassLoader().getClass() + "Loaded"); }}Copy the code
This class is compiled, and I put the compiled class in the root directory of drive D, and then I delete the class file that was in the project. If I don’t delete the class file, using the parent delegate model, We know that this class will be loaded by sun.misc.Launcher$AppClassLoader.
Then we define a loading class of our own:
public class MyClassLoader extends ClassLoader {
public MyClassLoader(a){}
public MyClassLoader(ClassLoader parent){
super(parent);
}
protectedClass<? > findClass(String name)throws ClassNotFoundException {
File file = new File("D:\\ClassLoaderTest.class");
try{
byte[] bytes = getClassBytes(file);
The defineClass method converts a file consisting of binary stream bytes into a java.lang.classClass<? > c =this.defineClass(name, bytes, 0, bytes.length);
return c;
}
catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
private byte[] getClassBytes(File file) throws Exception {
// The.class bytes are read in here, so a byte stream is used
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);
while (true) {int i = fc.read(by);
if (i == 0 || i == -1)
break;
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
MyClassLoader classLoader = new MyClassLoader();
Class clazz = classLoader.loadClass("com.geekdigging.lesson03.classloader.ClassLoaderTest");
Object obj = clazz.newInstance();
Method helloMethod = clazz.getDeclaredMethod("hello".null);
helloMethod.invoke(obj, null); }}Copy the code
Final print result:
I am fromclass com.geekdigging.lesson03.classloader.MyClassLoaderThe loadedCopy the code
reference
www.cnblogs.com/ityouknow/p…
This article is constantly updated, you can search “Geek excavator” on wechat to read it in the first time, and the key words in reply have all kinds of tutorials PREPARED by me, welcome to read.