Class loading process
The main job of a class loader is to load class files into the JVM. As shown in the figure below, the process is divided into three steps:
1. Loading: locate the class file to be loaded and load its byte stream into the JVM;
2. Link: Allocate the most basic memory structure to load to save its information, such as properties, methods and referenced classes. At this stage, the class is still unavailable;
(1) Verification: Verify the loaded byte stream, such as format and security;
(2) Memory allocation: prepare memory space for this class to represent its properties, methods and referenced classes;
(3) Analysis: Load other classes referenced by this class, such as parent class, implemented interfaces, etc.
3. Initialization: Assign values to class variables.
The class loader level
The dotted line in the figure below are several important class loaders provided by JDK, and the detailed description is as follows:
(1) Bootstrap Class Loader: When starting a class containing the main function, load the jar package in the JAVA_HOME/lib directory or -Xbootclasspath specified directory;
(2) Extention Class Loader: Load the jar package in the JAVA_HOME/lib/ext directory or -Djava.ext.dirs specified directory.
(3) System Class Loader: Load classpath or -Djava.class.path class or jar package in the specified directory.
What you need to know is:
1. In addition to Bootstrap Class Loader, other class loaders are subclasses of java.lang.ClassLoader class;
2. Bootstrap Class Loader is not implemented in Java. If you do not use a personalized class loader, then java.lang.String.class.getClassLoader() is null, and the parent loader of Extension Class Loader is also null;
3. Several ways to obtain class loaders:
(1) Obtaining Bootstrap Class Loader: What you get must be null when trying to get Bootstrap Class Loader. You can verify it in the following way: use the getClassLoader method of class objects in the rt.jar package, such as java.lang.String.class.getClassLoader() to obtain or obtain the Extention Class Loader, and then call the getParent method to obtain;
(2) Obtain the Extention Class Loader: use the getClassLoader method of the class object in the jar package under the JAVA_HOME/lib/ext directory or first obtain the System Class Loader, and then obtain it through its getParent method;
(3) Obtain System Class Loader: Call the getClassLoader method of the class object containing the main function, or call Thread.currentThread().getContextClassLoader() or call ClassLoader.getSystemClassLoader() within the main function;
(4) Obtain User-Defined Class Loader: Call the getClassLoader method of the class object or call Thread.currentThread().getContextClassLoader();
Operational principles of class loader
1. Agent Principle
2. Visibility principle
3. The principle of uniqueness
4. Proxy principle The proxy principle refers to a class loader requesting its parent loader proxy to load when loading a class, and the parent loader will also request its parent loader proxy to load, as shown in the figure below.
Why use proxy mode? First of all, this can reduce duplicate loading of a class. (Is there any other reason?)
Where to misunderstand:
Generally, it is thought that the proxy order of the class loader is Parent First, that is:
1. When loading a class, the class loader first checks whether it has loaded the class. If it has been loaded, it will return; otherwise, ask the parent loader to proxy;
2. The parent loader repeats the operation of 1 until the Bootstrap Class Loader;
3. If the Bootstrap Class Loader does not load the class, it will try to load it, and if it is successful, it will return; if it fails, a ClassNotFoundException is thrown, it will be loaded by the child loader;
4. The subclass loader catches the exception and tries to load it. If it succeeds, it returns. If it fails, it throws a ClassNotFoundException until the loaded subclass loader is initiated.
This understanding is correct for loaders such as Bootstrap Class Loader, Extention Class Loader, and System Class Loader, but some personalized loaders are not the case. For example, some class loaders implemented by IBM Web Sphere Portal Server are Parent Last, which is the child loader that attempts to load first. If the load fails, the parent loader will be asked. The reason for this is: if you expect a certain version of log4j to be used by all applications, put it in the WAS_HOME library, and it will be loaded when WAS starts. If an application wants to use another version of log4j, it cannot be achieved if it uses Parent First because the class in log4j is already loaded in the parent loader. However, if Parent Last is used, the class loader responsible for loading the application will prioritize loading another version of log4j.
Visibility principle
The visibility of each class to the class loader is different, as shown in the figure below.
To extend knowledge, OSGi takes advantage of this feature. Each bundle is loaded by a separate class loader, so each class loader can load a version of a certain class, so the entire system can use multiple versions of a class.
The principle of uniqueness
Each class is loaded at most once in a loader.
Extended Knowledge 1: To be precise, the singleton pattern refers to a singleton object that only one copy of a class in a set of class loaders.
Extended Knowledge 2: A class can be loaded by multiple class loaders. Each class object is in its own namespace. When comparing the class object or type converting the instance, it will compare its respective namespace at the same time, such as:
The Klass class is loaded by ClassLoaderA, assuming the class object is KlassA; it is loaded by ClassLoaderB, assuming the class object is KlassB, then KlassA is not equal to KlassB. At the same time, when the instance of ClassA is cast to KlassB, a ClassCastException exception will be thrown.
Why do you need a personalized class loader? Personalized class loader adds a lot of flexibility to the Java language. The main uses are:
1. Classes can be loaded from multiple places, such as on the network, in the database, or even instantly compiled source files;
2. After personalization, the class loader can load a certain version of the class file in principle at runtime;
3. After personalization, the class loader can dynamically unload some classes;
4. After personalization, the class loader can decrypt and decompress the class before loading the class.
Implicit and explicit loading of classes
Implicit loading: When a class is referenced, inherited or instantiated, it will be loaded implicitly. If the load fails, NoClassDefFoundError is thrown.
Explicit loading: Use the following method, if the load fails, a ClassNotFoundException will be thrown.
cl.loadClass(), cl is an instance of the class loader;
Class.forName(), loads using the class loader of the current class.
If the execution of the static block of the class has the following classes:
package cn.fengd; public class Dummy { static { System.out.println("Hi"); } } Create another test class:
package cn.fengd; public class ClassLoaderTest { public static void main(String[] args) throws InstantiationException, Exception { try { /* * Different ways of loading. */ Class c = ClassLoaderTest.class.getClassLoader().loadClass("cn.fengd.Dummy"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }How effective is it after running?
What if it is replaced by Class.forName("cn.fengd.Dummy"); or new Dummy()?
Hi will be output.
Frequently Asked Questions:
1. Is the specified type loaded by different class loaders still the same type?
In Java, a class uses its fully qualified class name as its identifier. The exact match class name referred to here includes the package name and the class name. However, in the JVM, a class uses its full name and an instance of the loading class ClassLoader as unique identifiers. The classes loaded by different class loaders will be placed in different namespaces. We can use two custom class loaders to load a custom type (note that do not place the bytecode of the custom type into the system path or extension path, otherwise it will be loaded first by the system class loader or extension class loader), and then use the two Class instances obtained to make java.lang.Object.equals (…) judgments, and you will get unequal results. This can be done by writing two custom class loaders to load the same custom type and then make a judgment; at the same time, you can test the loading of java.* type, and then compare and test the test results.
2. Directly call the Class.forName (String name) method in the code. Which class loader will trigger the class loading behavior?
Class.forName(String name) will use the class loader that calls the class to load class by default. Let's analyze the corresponding jdk code directly:
//java.lang.Class.java publicstatic Class<?> forName(String className)throws ClassNotFoundException {return forName0(className, true, ClassLoader.getCallerClassLoader());}//java.lang.ClassLoader.java// Returns the invoker's class loader, or null if none.static ClassLoader getCallerClassLoader() { // Get the type of calling class (caller) Class caller = Reflection.getCallerClass(3); // This can be null if the VM is requesting it if (caller == null) { returnnull; } // Call the local method in java.lang.Class to get the ClassLoader that loads the calling class (caller) return caller.getClassLoader0();}//java.lang.Class.java//Native implementation of the virtual machine to get the class loader of the current class native ClassLoader getClassLoader0(); 3. When writing a custom class loader, if the parent loader is not set, then what is the parent loader?
When the parent class loader is not specified, the system class loader is used by default. Some people may not understand, now let’s take a look at the corresponding code implementation of JDK. As we all know, we write a custom class loader directly or indirectly inherit from the java.lang.ClassLoader abstract class. The corresponding default constructor without parameters is implemented as follows:
//Excerpt from java.lang.ClassLoader.javaprotected ClassLoader() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); } this.parent = getSystemClassLoader(); initialized = true;}Let's take a look at the corresponding implementation of the getSystemClassLoader() method:
privatestaticsynchronizedvoid initSystemClassLoader() { //... sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); scl = l.getClassLoader(); //...}We can write simple test code to test it:
System.out.println(sun.misc.Launcher.getLauncher().getClassLoader());
The corresponding output of this machine is as follows:
sun.misc.Launcher$AppClassLoader@197d257
So, we can now believe that when the custom class loader does not specify the parent class loader, the default parent class loader is the system class loader. At the same time, we can draw the following conclusions:
Instant user-defined class loader does not specify the parent class loader, then the classes in the following three places can also be loaded:
(1) Classes under <Java_Runtime_Home>/lib
(2) Classes in the location specified by the system variable java.ext.dir
(3) Classes under the current project class path or in the location specified by the system variable java.class.path
4. When writing a custom class loader, what will happen if the parent class loader is forced to be null? If the custom class loader cannot load the specified class, will it definitely fail?
The JVM specification stipulates that if the user-defined class loader forces the parent class loader to null, the startup class loader will be automatically set to the parent class loader of the current user-defined class loader (this problem has been analyzed before). At the same time, we can draw the following conclusions:
If the instant user-defined class loader does not specify the parent class loader, then it can also load the class under <Java_Runtime_Home>/lib, but the class under <Java_Runtime_Home>/lib/ext directory cannot be loaded at this time.
Note: The inferred conclusions of Questions 3 and 4 are based on the user-defined class loader itself that continues the default delegation logic of java.lang.ClassLoader.loadClass(…). If the user changes this default delegation logic, the above inferred conclusion may not be valid. See question 5 for details.
5. What are the general points of attention when writing custom class loaders?
(1) Generally, try not to override the delegation logic in the existing loadClass (…) method. Generally, it is done in versions before JDK 1.2, and it turns out that doing so is very likely to cause the system's default class loader to not work properly. In the JVM specification and JDK documentation (1.2 or later versions), it is not recommended that users overwrite the loadClass(…) method. In contrast, developers are clearly reminded to overwrite the findClass(…) logic when developing a custom class loader. Take an example to verify the problem:
//User custom class loader WrongClassLoader.Java (overwrite loadClass logic) publicclassWrongClassLoaderextends ClassLoader { public Class<?> loadClass(String name) throws ClassNotFoundException { returnthis.findClass(name); } protected Class<?> findClass(String name) throws ClassNotFoundException { //Suppose here is just to a specific directory other than the project D: /library to load the class specific implementation code omitted}}Through the previous analysis, we already know that the default of the user-defined class loader (WrongClassLoader)
The recognized class loader is a system class loader, but the conclusions of the four types of problems are not valid now. Everyone can
Just test it out, now <Java_Runtime_Home>/lib, <Java_Runtime_Home>/lib/ext and
The classes on the program class path cannot be loaded.
Question 5 Test Code 1
publicclass WrongClassLoaderTest { publicstaticvoid main(String[] args) { try { WrongClassLoader loader = new WrongClassLoader(); Class classLoaded = loader.loadClass("beans.Account"); System.out.println(classLoaded.getName()); System.out.println(classLoaded.getClassLoader()); } catch (Exception e) { e.printStackTrace(); } }}(Note: D: "classes"beans"Account.class physically exists)
Output result:
java.io.FileNotFoundException: D:"classes"java"lang"Object.class (The system cannot find the specified path.) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:106) at WrongClassLoader.findClass(WrongClassLoader.java:40) at WrongClassLoader.loadClass(WrongClassLoader.java:29) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.lang.ClassLoader.defineClass(ClassLoader.java:400) at WrongClassLoader.findClass(WrongClassLoader.java:43) at WrongClassLoader.loadClass(WrongClassLoader.java:29) at WrongClassLoaderTest.main(WrongClassLoaderTest.java:27)Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.lang.ClassLoader.defineClass(ClassLoader.java:400) at WrongClassLoader.findClass(WrongClassLoader.java:43) at WrongClassLoader.loadClass(WrongClassLoader.java:29) at WrongClassLoaderTest.main(WrongClassLoaderTest.java:27)
This means that even the supertype java.lang.Object of the type to be loaded cannot be loaded. The logical errors listed here caused by overwriting loadClass (…) are obviously relatively simple, and the logical errors caused in fact may be much more complicated.
Question 5 Test 2
//User custom class loader WrongClassLoader.Java (do not overwrite loadClass logic) publicclassWrongClassLoaderextends ClassLoader { protected Class<?> findClass(String name) throws ClassNotFoundException { //Suppose here is just to a specific directory other than the project D: /library to load the class specific implementation code omitted}}After modifying the custom class loader code WrongClassLoader.Java, run the test code, and the output result is as follows:
beans.AccountWrongClassLoader@1c78e57
This shows that beans.Account is loaded successfully and is loaded by the custom class loader WrongClassLoader.
I don’t think there is any need to explain the reasons for this, and you should be able to analyze it.
(2) Correctly set up the parent class loader through the analysis of question 4 and question 5 above. I personally think this is the most important point when customizing user class loaders, but it is often ignored or easily brought over. With the analysis of the previous JDK code as the basis, I think everyone can give any examples now.
(3) Ensure the logical correctness of the findClass (String) method, try to accurately understand the loading tasks to be completed by the class loader to be defined, and ensure that the corresponding bytecode content can be obtained to the greatest extent.
6. How to determine which paths the system class loader can load?
First, you can directly call ClassLoader.getSystemClassLoader() or other methods to get the system class loader (the system class loader and extension class loader themselves are derived from URLClassLoader), and you can get it by calling the getURLs() method in URLClassLoader;
Second, you can directly view the entry information on the current classpath by obtaining the system attribute java.class.path. System.getProperty("java.class.path")
7. How to determine which paths the standard extension class loader can load?
Method One:
try { URL[] extURLs = ((URLClassLoader)ClassLoader.getSystemClassLoader().getParent()).getURLs(); for (int i = 0; i < extURLs.length; i++) { System.out.println(extURLs[i]); } } catch (Exception e) {//…}The corresponding output of this machine is as follows:
file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/dnsns.jarfile:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/localedata.jarfile:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jarfile:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar