-
class Loading Verification Process
-
Load
The first stage of loading classes Get the binary stream of the class Conversion to method area data structure Generate corresponding java.lang.Class objects in the Java heap
-
link
-
Verification
-
Get ready
-
analysis
-
-
Initialization
Execution class constructor < clinit > static variable assignment statement static {} statement Make sure that < clinit > of the parent class is invoked before < clinit > of the subclass is invoked < clinit > is thread-safe
-
-
What is ClassLoader
ClassLoader is an abstract class An instance of ClassLoader will read into Java bytecode and load the class into the JVM ClassLoader can be customized to meet different bytecode stream acquisition methods ClassLoader is responsible for the loading phase of the class loading process
-
ClassLoader default design pattern in JDK
-
Method
The Important Method of ClassLoader public Class<?> loadClass(String name) throws ClassNotFoundException Load and return a Class protected final Class<?> defineClass(byte[] b, int off, int len) Define a class that is not called publicly protected Class<?> findClass(String name) throws ClassNotFoundException The loadClass callback method customizes the recommended practices of ClassLoader protected final Class<?> findLoadedClass(String name) Find classes that have been loaded
-
classification
BootStrap ClassLoader (Start ClassLoader) Extension Class Loader App ClassLoader (Applying ClassLoader/System ClassLoader) Custom Class Loader Each ClassLoader has a Parent as its father
-
Cooperative work
-
Loading order of test classes
public class FindClassOrder { public static void main(String args[]){ HelloLoader loader=new HelloLoader(); loader.print(); } }
public class HelloLoader { public void print(){ System.out.println("I am in apploader"); } }
Run the above code directly: I am in apploader
Add the parameter - Xbootclasspath/a:/Users/heliming/IdeaProjects/democloud/jvm/src/main/java
//Compile the class file of this java file into the / Users/heliming/IdeaProjects/democloud/jvm/src/main/java directory public class HelloLoader { public void print(){ System.out.println("I am in bootloader"); } }
I am in bootloader HelloLoader will not be loaded in AppLoader at this time I am in apploader is not loaded in classpath Explain that class loading is from top to bottom
-
When testing to find classes, it's from the bottom up
Force loading in apploader
/** * description: https://www.cnblogs.com/cl-rr/p/9081817.html defineClass()Method is more used to load files that are no longer classes, or to overwrite the bytecode of the original class when AOP. It should be noted that for the same class, use defineClass() twice or more to throw an exception. * * @author: dawn.he QQ: 905845006 * @email: dawn.he@cloudwise.com * @email: 905845006@qq.com * @date: 2019/9/24 6:22 PM */ //package com.zejian.classloader; import java.io.*; import java.lang.reflect.Method; /** * Created by zejian on 2017/6/21. * Blog : http://blog.csdn.net/javazejian [The original address, please respect the original] */ public class FileClassLoader extends ClassLoader { private long lastTime; private String rootDir; public FileClassLoader(String rootDir) { this.rootDir = rootDir; } /** * Writing the logic of findClass method * @param name * @return * @throws ClassNotFoundException */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // Gets the class file byte array of the class byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { //Generating class objects directly return defineClass(name, classData, 0, classData.length); } } /** * Write the logic to get the class file and convert it to a bytecode stream * @param className * @return */ private byte[] getClassData(String className) { // Read the bytes of the class file String path = classNameToPath(className); try { InputStream ins = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; // Read the bytecode of the class file while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * Full path of class file * @param className * @return */ private String classNameToPath(String className) { return rootDir + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; } public static void main(String[] args) throws ClassNotFoundException { // String rootDir="/Users/zejian/Downloads/Java8_Action/src/main/java/"; String rootDir="/Users/heliming/IdeaProjects/democloud/jvm/target/classes/"; //Create a custom file class loader FileClassLoader loader = new FileClassLoader(rootDir); try { //Load the specified class file // Class<?> object1=loader.loadClass("com.zejian.classloader.DemoObj"); Class<?> object1=loader.loadClass("HelloLoader"); Object o = object1.newInstance(); Method method=o.getClass().getDeclaredMethod("print", null); method.invoke(o, null); //Output: I am in apploader } catch (Exception e) { e.printStackTrace(); } } }
Printing: I am in apploader
When looking for classes, first look at the underlying Loader, from bottom to top. If Apploader can find it, it won't go to the upper loader to load it.
-
Replace the main function above, and test findClass can only be loaded once
public static void main(String[] args) throws ClassNotFoundException { String rootDir = "/Users/heliming/IdeaProjects/democloud/jvm/target/classes/"; //Create a custom file class loader FileClassLoader loader = new FileClassLoader(rootDir); FileClassLoader loader2 = new FileClassLoader(rootDir); try { Class<?> object1 = loader.loadClass("HelloLoader"); Object o = object1.newInstance(); Method method = o.getClass().getDeclaredMethod("print", null); method.invoke(o, null); Class<?> object2 = loader2.loadClass("HelloLoader"); o = object1.newInstance(); method = o.getClass().getDeclaredMethod("print", null); method.invoke(o, null); System.out.println("loadClass->obj1:" + object1.hashCode()); System.out.println("loadClass->obj2:" + object2.hashCode()); //Load the specified class file, call findClass(), bypass the detection mechanism, and create different class objects. Class<?> object3 = loader.findClass("HelloLoader"); //findClass can only be loaded once. If it is reloaded again, it will cause an error to reload the class. //Class<?> object5 = loader.findClass("HelloLoader"); Class<?> object4 = loader2.findClass("HelloLoader"); System.out.println("loadClass->obj3:" + object3.hashCode()); System.out.println("loadClass->obj4:" + object4.hashCode()); /** * Output results: * loadClass->obj1:644117698 loadClass->obj2:644117698 findClass->obj3:723074861 findClass->obj4:895328852 */ } catch (Exception e) { e.printStackTrace(); } }
-
-
The Problem of Parent Delegation Model
-
Solve:
Thread. setContextClassLoader() Context loader It's a role. To solve the problem that the top-level ClassLoader cannot access the classes of the bottom-level ClassLoader The basic idea is that in the top-level ClassLoader, an instance of the underlying ClassLoader is passed in.
It can be seen from the graph that rt.jar core package is loaded by Bootstrap class loader. It contains SPI core interface classes. Because classes in SPI often need to call methods of external implementation classes, while jdbc.jar contains external implementation classes (jdbc.jar exists in classpath path path path) which cannot be loaded by Bootstrap class loader, it can only delegate threads. The following class loader loads the implementation classes in jdbc. jar into memory for SPI-related classes to use. Obviously, this method of loading thread context class loader destroys the "Parent Delegation Model". It abandons the Parent Delegation Load Chain mode in the execution process, so that the program can use the class loader in reverse, which also makes the Java class loader more flexible. To further validate this scenario, look at the source code of the DriverManager class, which is a class in the Java core rt.jar package that manages the implementation drivers of different databases, namely Driver, which implements the java.sql.Driver interface in the Java core package, such as com.mysql.jdbc.Driver in the MySQL driver package, here Mainly see how to load the external implementation class. The following code will be executed during drivermanager initialization
//DriverManager is a class of the Java core package rt.jar public class DriverManager { //Eliminate unnecessary code static { loadInitialDrivers();//Execute this method println("JDBC DriverManager initialized"); } //Load Initial Drivers Method private static void loadInitialDrivers() { sun.misc.Providers() AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //Loading external Driver implementation classes ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); //Eliminate unnecessary code... } }); }
The loadInitialDrivers() method is executed when the DriverManager class is initialized. In this method, the driver class is loaded by ServiceLoader.load(Driver.class); to load the driver class implemented externally, the ServiceLoader class reads the contents of META-INF file under mysql's jdbc.jar, as shown below.
The inheritance classes of com.mysql.jdbc.Driver are as follows:
public class Driver extends com.mysql.cj.jdbc.Driver { public Driver() throws SQLException { super(); } static { System.err.println("Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. " + "The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary."); } }
As can be seen from the comments, the usual use of com.mysql.jdbc.Driver has been discarded and replaced by com.mysql.cj.jdbc.Driver, which means that the authorities no longer recommend that we register MySQL drivers with the following code
//Registering driver classes in this way is not recommended Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/cm-storylocker?characterEncoding=UTF-8"; // Getting database connections through java Libraries Connection conn = java.sql.DriverManager.getConnection(url, "root", "root@555");
Instead, the registration steps are removed directly, as follows
String url = "jdbc:mysql://localhost:3306/cm-storylocker?characterEncoding=UTF-8"; // Getting database connections through java Libraries Connection conn = java.sql.DriverManager.getConnection(url, "root", "root@555");
This way ServiceLoader will help us deal with everything and ultimately load through the load() method to see how the load() method is implemented.
public static <S> ServiceLoader<S> load(Class<S> service) { //Loading through the thread context class loader ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
Obviously, it is loaded by thread context class loader. In fact, the SPI class of core package loads external implementation class based on thread context class loader. In this way, Java core code calls external implementation class internally. We know that the thread context class loader is AppClassLoader by default, so why not get the class loader directly through getSystem ClassLoader () to load classes under the classpath path path path path? Actually, it's feasible, but one drawback of using getSystemClassLoader() directly to get AppClassLoader loaded classes is that there will be problems when code is deployed to different services, such as Java Web application services or services such as EJB, because these services use thread contexts. Class loaders are not AppClassLoaders, but Java Web applications are their own class loaders. Class loaders are different. So we should use less getSystem ClassLoader (). In short, different services may use different default ClassLoaders, but using thread context class loaders can always get the same ClassLoader as the current program execution, thus avoiding unnecessary problems. ok ~. Talk about the thread context class loader for the moment. The DeriveManager class described above, you can look at the source code by yourselves, I believe you will have more experience. In addition, this article does not elaborate too much about Service Loader. After all, our topic is class loader, but Service Loader is a very good decoupling mechanism. Home can refer to its relevant usage.
-
-
Breaking the Conventional Model and Hot Replacement
Destruction of Parental Patterns Parent mode is the default mode, but it is not necessary to do so. Tomcat's Webapp Class Loader loads its own Class first, fails to find it, and then delegates parent. OSGi's lassLoader forms a mesh structure that freely loads Class as needed
In the java directory
javac Worker.java Start main function Modify Worker.java Again, javac Worker.java Output changed
HelloMain.java
import java.io.File; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; public class HelloMain { private URLClassLoader classLoader; private Object worker; private long lastTime; // private String classDir="/Users/heliming/IdeaProjects/democloud/jvm/target/classes/"; private String classDir="/Users/heliming/IdeaProjects/democloud/jvm/src/main/java/"; public static void main(String[] args) throws Exception { HelloMain helloMain=new HelloMain(); helloMain.execute(); } private void execute() throws Exception { while(true){ //Monitor whether loading is required if(checkIsNeedLoad()){ System.out.println("New version detected, ready to reload"); reload(); System.out.println("Reload completed"); } //One second invokeMethod(); Thread.sleep(1000); } } private void invokeMethod() throws Exception { //Called by reflection //The main reason for using reflection is that you don't want Work to be loaded by appclassloader. // If loaded by app classloader, loading through custom loader can be a bit problematic Method method=worker.getClass().getDeclaredMethod("sayHello", null); method.invoke(worker, null); } private void reload() throws Exception { classLoader = new MyClassLoader(new URL[] { new URL( "file:"+classDir)}); worker = classLoader.loadClass("Worker") .newInstance(); System.out.println(worker.getClass()); } private boolean checkIsNeedLoad() { File file=new File(classDir+ "Worker.class"); long newTime=file.lastModified(); if(lastTime<newTime){ lastTime=newTime; return true; } return false; } }
Worker.java
public class Worker { public void sayHello(){ System.out.println("version: fds"); } }
MyClassLoader.java
import java.net.URL; import java.net.URLClassLoader; public class MyClassLoader extends URLClassLoader { public MyClassLoader(URL[] urls) { super(urls); } // Break parental mode to ensure that your classes are loaded by your classloader @Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class c = findLoadedClass(name); if (c == null) { try { //Here, if you load the object class first and you can't find it, you will get an error, so under catch c=findClass(name); } catch (Exception e) { } } if(c==null){ c=super.loadClass(name, resolve); } return c; } }
Deep into the jvm kernel - Principle, diagnosis and Optimization - 6. Class loader
Posted by danf_1979 on Wed, 25 Sep 2019 00:07:43 -0700