After the Java compiler compiles the. Class file, we need to use JVM to run the class file. The first task is to input the bytecode from the disk to the memory. This process is called 【Load]. After loading, we can perform a series of preparations before running, such as opening up space for static variables, store the constant pool in the Method Area Memory, parse the address of the constant pool, and initialize static variables. In this article, we will talk about how the JVM loads class files?
1. JVM class loading process
When we use commands to execute a Java program (such as test. Class): Java Test
(1) java.exe will help us find JRE, and then find JVM. dll located inside JRE. This is the real Java Virtual Machine. Finally, load the dynamic library and activate the Java Virtual Machine.
(2) After the virtual machine is activated, initialization will be performed first, such as reading system parameters. Once the initialization is complete, the firstClass Loader-- Bootstrap loader (Start the Class Loader).
(3) In the initial work of bootstrap loader, apart from some basic initialization actions, the most important thing is to load the extclassloader (Extended Class Loader)And set its parent to null, which indicates that its parent loader is bootstraploader.
(4) Then bootstrap loader needs to load the appclassloader (User-Defined Class LoaderAnd set its parent to the previously generated extclassloader object. Both loaders exist in the form of static classes.
It should be noted that launcher $ extclassloader. Class and launcher $ appclassloader. Class are both loaded by bootstrap loader, so there is no relationship between parent and which class loader is loaded.
It is difficult for beginners to understand this process. The following describes in detail what the class loader and "parent" are.
2. architecture of the Class Loader
The JVM must load the class file through a program called the Class Loader. Its function is to load the byte code stream of the Code to be run into the memory (JVM-managed method zone) from the disk file. The following are several important concepts:
(1) Start the Class Loader:Each Java virtual machine must have a startup class loader. It is only responsible for finding the class to be loaded in the installation path of the system class (the class file of the core Java API. The implementation of this loader is written by C ++ and is part of the JVM implementation.
(2) extended class loaders and custom class loaders:It is responsible for loading class files other than core Java APIs. For example, it is used to install or download standard extended class files, class files of class libraries found in the class path, and class files used for application program running. Note: The Custom class loader is not implemented by the application programmer. It is also a JVM
(3) namespace:The Java Virtual Machine maintains a unique namespace for each class loader. A Java program can load multiple classes with the same fully qualified name multiple times. The Java virtual machine must determine the uniqueness of the "multiple classes". Therefore, when multiple class loaders have classes of the same name, to uniquely identify the class, add the ID of the Class Loader that loads the class to the class name (indicating the namespace where the class is located ). Displays the namespaces related to the two class loaders. Obviously, different class loaders allow loading the same class volcano.
Namespaces are helpful for security implementation, because you can effectively set a shield between classes loaded into different namespaces. In a Java virtual machine, classes in the same namespace can directly interwork with each other, while classes in different namespaces cannot even detect each other, unless explicit mechanisms are provided to allow them to interact. Once loaded, if a malicious class is granted the permission to access the current class loaded by other virtual machines, it can potentially know information that it should not know, or interfere with the normal operation of the program.
3. Parental principal Model
User-Defined class loaders often rely on other class loaders-at least on the startup class loaders created during VM startup-to help them implement some class loading requests :. before Version 1.2, non-start class loaders must explicitly turn to other class loaders, which can request another user-defined class loader to load a class, this request is implemented by calling loadclass () for the requested user-defined class loader. In addition, the class loader can also call findsystemclass () to request the class loader to load the class. This is a static method in the class classloader.
In version 1.2, the process of classloaders requesting another classloader to load types is formally called the parent-parent delegation mode.
Starting from version 1.2, each class loader except the start class loader has a "parent" class loader, before a particular class loader tries to load the type in common ways, it first delegates the task to its parent by default, asking its parent to load this type. The parent asks its own parent to load this type in sequence. This delegation continues until it reaches the start class loader. Generally, the start class loader is the last class loader in the delegation chain. If the parent class loader of A Class Loader has the ability to load this type. The Class Loader returns this type. Otherwise, the Class Loader tries to load the class by itself.
When the Java Virtual Machine starts running, it can create at least one custom loader or multiple loaders before the application starts. all these loaders are connected to a parent-child delegate chain, at the top of which is the startup class loader.
For example, suppose you write an application and run it on a virtual machine. the Virtual Machine instantiates two user-defined class loaders at startup: an extension class loader and a class path class loader ". these class loaders and the start class loaders are joined together in a parent-child delegate chain, as shown in.
The parent of the Class Loader is the extension class loader, and the parent of the extension class loader is the start class loader. in Figure 2, the class path loader is used as the system class loader by the instance. suppose your program instantiates its Network Class Loader, which specifies the system class loader as its parent.
The following routine describes the parent-child relationship of the class loader.
package test; import java.net.URL; import java.net.URLClassLoader; public class ClassLoaderTest { private static int count = -1; public static void testClassLoader(Object obj) { if (count < 0 && obj == null) { System.out.println("Input object is NULL"; return; } ClassLoader cl = null; if (obj != null && !(obj instanceof ClassLoader)) { cl = obj.getClass().getClassLoader(); } else if (obj != null) { cl = (ClassLoader) obj; } count++; String parent = ""; for (int i = 0; i < count; i++) { parent += "Parent "; } if (cl != null) { System.out.println( parent + "ClassLoader name = " + cl.getClass().getName()); testClassLoader(cl.getParent()); } else { System.out.println( parent + "ClassLoader name = BootstrapClassLoader"; count = -1; } } public static void main(String[] args) { URL[] urls = new URL[1]; URLClassLoader urlLoader = new URLClassLoader(urls); ClassLoaderTest.testClassLoader(urlLoader); } }
The output of the preceding routine is:
Classloader name = java.net. urlclassloader
Parent classloader name = sun. Misc. launcher $ appclassloader
Parent classloader name = sun. Misc. launcher $ extclassloader
Parent classloader name = bootstrapclassloader
Request Process of the Class Loader
The preceding example shows how to change the main method:
Classloadertest Tc = new classloadertest ();
Classloadertest. testclassloader (TC );
Output:
Classloader name = sun. Misc. launcher $ appclassloader
Parent classloader name = sun. Misc. launcher $ extclassloader
Parent classloader name = bootstrapclassloader
When the program is running, the class path loader sends a request to load the class classloadertest, the class path loader must first ask its parent --- extension class loader --- to find and load this class. Similarly, the extension class loader first asks the start class loader. Because classloadertest is neither a class in Java API (java_home \ JRE \ Lib) nor an extension path (java_home \ JRE \ Lib \ ext) installed, both types of loaders will be returned without providing a loaded class named classloadertest to the class path class loader. The class path loader can only be loaded in its own way
Classloadertest, which downloads the class from the current class path. In this way, classloadertest can play a role in the execution after the application.
In the preceding example, the testclassloader method of the classloadertest class is called for the first time. This method references the java. Lang. string class in the Java API. The Java Virtual Machine requests the classloadertest class loader to load java. Lang. String. Just as before, the class path loader first passes the request to its parent class loader, and then the request is delegated all the way to the startup class loader. However, the class loader can. lang. the string class is returned to the class path Class Loader because it can find this class, so that the extension class loader does not have to find it in the already installed extension path.
This class does not have to be found in the class path. The extension class loader and the class path Class Loader only need to return the class java. Lang. String returned by the startup class loader. From this moment on, no matter when the classloadertest class references a class named java. Lang. String, the virtual machine can directly use this java. Lang. string class.
4. A classic instance description
Let's look at the following code:
package java.lang;public class String {public static void main(String[] args){}}
What are the differences? By the way, we have written a class that is exactly the same as the string in JDK, and even the java. lang package is the same. The only difference is that our custom string class has a main function. Let's run it:
Java. Lang. nosuchmethoderror: Main
Exception in thread "Main"
Why? Isn't there a main method in our string class?
In fact, contact the parent-parent delegation model we mentioned above to explain this problem.
Run this Code. The JVM will first create a custom Class Loader called appclassloader and link the loader to the delegate chain: appclassloader-> extclassloader-> bootstraploader.
Then, appclassloader delegates the request for loading Java. Lang. string to extclassloader, and extclassloader delegates it to the final start class loader bootstraploader.
The bootstraploader can only load the class (j2se API) in java_home \ JRE \ Lib. The problem is that there is indeed a Java in the standard API. lang. string (note that this class and our custom class are completely two classes ). Bootstraploader thought that he had found this class and did not hesitate to load java. Lang. String in j2se API.
Finally, the above loading error occurs (note that it is not an exception, it is an error, and JVM exits), because the string class in the API does not have the main method.
Conclusion: Of course we can define a class that is exactly the same as the API, but because of the parent-child delegate model, we cannot load the class we have customized. Therefore, the j2se specification requires that our custom packages have their own unique characteristics (network domain names ). Another point is that this loader makes the JVM more secure to run programs, because it is difficult for hackers to replace the code in the API at will.