In-depth understanding of the Java Virtual Machine – JVM class loading process and class loaders

Not only for the interview, but also for the fundamental learning and understanding of Java code execution process, to improve their understanding of Java

Java Virtual Machine lifecycle:

  1. Normal end of program
  2. Abnormal program termination
  3. Operating system error
  4. System.exit()

Class loading

Add idea property print loaded class -xx :+TraceClassLoading

In Java code, classes are loaded, wired, and initialized at run time, and each class is added to the JVM (in the heap) by the classloader, forming a Java type that the virtual machine can use directly

1. The load

Load the Java bytecode as a binary stream, read it into memory, and place it in the method area of the runtime data area; Create a java.lang.Class object that describes the data structure of the Class

You can load class files from disk, JAR, WAR, network, or your own class file

2. The connection

It is divided into three stages: verification, preparation and analysis

(1)validation

Ensure that the Class load is correct, that the byte stream of the Class file does not affect the virtual machine security (because Class files can be generated from any source), that VerifyError is raised when validation fails, and that the binary stream in memory is put into the method section of the JVM’s runtime data section upon validation

  1. File format validation

The magic number at the beginning of the file represents the JDK version number and other information; Whether there are unsupported constants in the constant pool

The binary byte stream will enter the method area store in memory only if the authentication is successful

  1. Metadata validation

Verify that the class has a parent class and that the parent class inherits a class that is not allowed to inherit (final class); Whether the parent class or method required in the interface is implemented; Does the method field in the class match the parent class or interface (parameter type, return value type)

  1. Bytecode verification

Validates the method body of a class to ensure that type conversions are safe.

By default, bytecode authentication is not always secure. By Halting Problem, none of the programs checked by Halting all programs.

  1. Symbolic reference verification

Occurs when a symbolic reference is converted to a direct reference

Ensure that the symbolic reference can find the corresponding class.

(2)To prepare

Allocate memory (the method area in memory) for the static variable of the class and initialize it to a default value (not a self-set value, for example int a=1; Assign a to 0)

(3)parsing

Convert symbolic references in the virtual machine constant pool (a set of symbols describing target references, that is, references in the JVM) to direct references (the actual memory address pointing to the target)

3. The initialization

  • Passive use does not result in class initialization

To assign an initial value to a static variable, execute the static block

Initialization is triggered when:

  1. encounternew.getstatic.putstatic.invokestaticCommand is initialized if it is not
  2. Reflection callsreflectPackage, the call class is initialized
  3. When the virtual machine starts, it needs to specify a main class for execution. The main function class is initialized
  4. When a class is initialized, the parent class is initialized
  5. In the JDK7 MethodHandler

For static fields, only those directly defined are initialized

public class Test8 { public static void main(String[] args) { System.out.println(Son2.s); } } class Father2{ public static int s = 1; static{ System.out.println("hello i am father"); }} class Son2 extends Father2{static {system.out.println ("hello I am son"); }}Copy the code

To initialize a class, the parent class is required to have been initialized

When an interface is initialized, its parent interface is not required to be initialized

When a class is initialized, its implementation interface is not required to be initialized

Interface variables do not need to use the public static final modifier

Example: Loading static variables and constants

public class Test1 { public static void main(String[] args) { System.out.println(MyChild.s); }} class MyParent{/** *} class MyParent{/** *} class MyParent{/** *} Public static final String s = "dx"; public static final String s = "dx"; static { System.out.println("hello i am my parent"); } } class MyChild extends MyParent{ static { System.out.println("i am my child"); }}Copy the code

Example: Interface initialization

/** * The parent interface is not required to be initialized when the interface is initialized. * Constants are not loaded if they are determined at compile time. Public class Test4 {public static void main(String[] args) {system.out.println (myInterfaceson.b); MyInterface{public static final int a = 5; } interface MyInterfaceSon extends MyInterface{public static final int b = new Random().nextint (10); //public static final int b = 10; }Copy the code

Example: Array of objects not loaded

Public class Test3 {public static void main(String[] args) {/* * MyParen4 is not loaded, */ MyParent3[] myParent = new MyParent3[10]; // Class [ltop.dzou.jvm.myparent3; // Array type flag [L fully qualified system.out.println (myparent.getClass ()));}} class MyParent3{static{  System.out.println("i am my parent3"); } }Copy the code

Example: initialization of a static constant

Public class Test5 {public static void main(String[] args) {public static void main(String[] args) { Initialize count1 to 0 singleton to null count2 to 0 * after initialization, invokespecial is called in order and the constructor is executed, Count1 =1 count2=1 count2=1 count2=1 putStatic count2= 0 count1=0 count2=0 */ Singleton singleton = Singleton.getInstance(); System.out.println(singleton.count1); System.out.println(singleton.count2); } } class Singleton{ public static int count1; private static Singleton singleton = new Singleton(); private Singleton(){ count1++; count2++; System.out.println(count1); System.out.println(count2); } public static int count2 = 0; public static Singleton getInstance(){ return singleton; }}Copy the code

Parental delegation mechanism

When loading a class, the system checks whether the class has been loaded from the bottom up. If it has not been loaded, the system tries to load the class from the top down. Object is first loaded by the launcher loader rt.jar

Inclusion relation:

A child loader contains a reference to a parent loader, even if both loaders belong to the same type of loader (for example, the same custom loader).

The ClassLoader constructor can pass a parent reference to the parent class’s ClassLoader, which will delegate to the parent class first when loaded

Interview questions:

Is it possible to customize a java.lang.System class?

A: No, because the custom System class will be delegated to the launcher class loader when it is loaded. The main method will not be found when the main function is executed based on the fully qualified name of the System class, because the custom System class will not be loaded

Public class System {public static void main(String[] args) {}} output: The main method is not found in the java.lang.System class, define the main method as: Public static void main (String [] args) or deployment headaches the application class must extend deployment headaches. The application. The applicationCopy the code

Advantages of parental delegation model:

  1. Keep the core library safe: If you all have your own loader to load, there will be lots of namespaces, there will be lots of the same classes, butIncompatible with each otherUse (different namespaces),Ensure that core classes are loaded first.
  2. JVM classes that are identical can exist, isolated from each other through namespaces, can exist together, and can be used in different namespaces.

Class loader profiling

Class loader

JVM Virtual machine class loaders: the initiator loader, extension class loader, and system loader

The class loader simply loads the class with a fully qualified name to generate a binary stream and converts it into an instance of a java.lang. class object

  • The loading of real classes is done bydefineClassDone, according to the Java Doc
Converts an array of bytes into an instance of class Class. Before the Class can be used it must be resolved.Copy the code

It converts a binary stream into a java.lang.Class object returned

The namespace

  • Every class loader has oneoneselfNamespace of.
  • Classes in the same namespace are visible to each otherThe namespace consists of the loader and all classes loaded by the parent loader.
  • No two classes in the same namespace have the same full name (including the package name of the class); In different namespaces,There may beTwo classes with the same full name (including the package name of the class) appear.

The class files loaded by the extension class loader need to be packed into jar packages

Change the system classloader directory: Change java.system.class.loader to custom

Command: Java – Djava. System. Class. Loader/custom loader class file path

methods

role

loadClass(String name)

The load name isnameClass, and the return result isjava.lang.ClassClass.
findClass(String name)

The lookup name isnameClass, and the return result isjava.lang.ClassClass.
findLoadedClass(String name)

The lookup name isnameIs the class that has been loadedjava.lang.ClassClass.
defineClass(String name, byte[] b, int off, int len) The byte arraybIs converted to a Java class, and the result returned isjava.lang.ClassClass. This method is declared asfinal.
resolveClass(Class c)

Link to the specified Java class.

{% qnimg jvm/4.png %}

Example: Reflection does not cause class initialization

public class Test9 { public static void main(String[] args) throws ClassNotFoundException { ClassLoader classLoader = ClassLoader.getSystemClassLoader(); // classLoader does not cause Class<? > c = classLoader.loadClass("top.dzou.jvm.class_load.D"); System.out.println("---------"); // Using reflection to load a Class results in active use of the Class, thus initializing the class.forname (" top.dzou.jvm.class_load.d "); System.out.println(c);; } } class D{ static { System.out.println("hello i am d"); }}Copy the code

Example: Implement a class loader

For a custom ClassLoader, we load the class by inheriting the ClassLoader class and calling the loadClass method of the subclass. The loadClass method automatically calls the findClass method for us, which needs to implement the custom loadClass and implement the defineClass method

public class Test10 extends ClassLoader{ private String fileExt = ".class"; private String path = null; public void setPath(String path) { this.path = path; } public Test10(){ super(); } @override protected Class<? > findClass(String s) throws ClassNotFoundException { byte[] data = loadClassData(s); Return defineClass(s,data,0,data.length); return defineClass(s,data,0,data.length); } public byte[] loadClassData(String fileName){InputStream in = null; ByteArrayOutputStream baos = null; byte[] data = null; try { fileName = fileName.replace(".","/"); in = new FileInputStream(new File(path+fileName+this.fileExt)); baos = new ByteArrayOutputStream(); int c = 0; while((c=in.read())! =-1){ baos.write(c); } data = baos.toByteArray(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { in.close(); baos.close(); } catch (IOException e) { e.printStackTrace(); } } return data; } public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException { Test10 loader = new Test10(); Loader.setpath ("/home/dzou/ Java /jvm-learning/target/classes/"); Class<? > c = loader.loadClass("top.dzou.jvm.class_load.Test9"); System.out.println("class:"+c); Object o = c.newInstance(); System.out.println(o); System.out.println(o.getClass().getClassLoader()); }}Copy the code

Note: According to parent mechanism, will be to the parent class to load and also is the system class loader to load, the system class loader to load is successful, it will not use our custom class loader, so we need to target. In the class file deletion, use our custom. The class file path that makes the system class loader loading failure, To use our custom class loader

Namespace usage

Loaders for two different instances load classes under different paths

public class Test13 { public static void main(String[] args) throws Exception { Test10 loader1 = new Test10(); Test10 loader2 = new Test10(); loader1.setPath("/home/dzou/Downloads/j/classes/"); loader2.setPath("/home/dzou/Downloads/a/"); Class<? > clazz2 = loader2.loadClass("top.dzou.jvm.class_load.Test1"); Class<? > clazz1 = loader1.loadClass("top.dzou.jvm.class_load.Test1"); Object o1 = clazz1.newInstance(); Object o2 = clazz2.newInstance(); System.out.println(o1.getClass().getClassLoader()); System.out.println(o2.getClass().getClassLoader()); System.out.println(o1==o2); }} Output: top.dzou.jvm.class_load.Test10@6f94fa3e top.dzou.jvm.class_load.Test10@1d44bcfa falseCopy the code

Inheritance relationships

The Launcher system and extension class loader class – > ExtClassLoader/AppClassLoader inner class – > URLClassLoader support through the loading path and jars – > permissions SecureClassLoader support to provide protection authority ( ->ClassLoader

Any two loaders can create parent-child relationships through constructors, even class loaders of the same class

Context classloader

The ContextClassLoader is intended to break the Java parent delegate model

Now that we know about class loaders, let’s look at one of the core loaders, the ContextClassLoader

We can get the current context classloader via thread.currentThread ().getContextClassLoader()

Through the Thread. CurrentThread (). SetContextClassLoader (this cl); To set the context classloader

Dependency rules: We know that each class will use its own class loader to load the dependent classes in the class. For example, class A references class B, so when loading class A, it will use the loader that loads A to load B, and every class we write is loaded by the system class loader (AppClassLoader), that

  • Why context classloaders?

For those of you who are familiar with SPI, JDBC and JAXP are implemented based on SPI, as we’ll see in the next section. Basically, the JDK provides interfaces, and service providers provide different implementations (JAR packages). When we use these SPI interfaces, We need to import the corresponding JAR package into the specified directory of the classpath, which may be lib, mysql-connectorj, etc., but our SPI interface is in rt.jar, which is loaded for us by the initiator class, so according to the dependency rules and parental delegation model, The JVM loads our interface implementation class using the same initiator loader that loads the interface class, but the different implementation classes of our SPI are in the classpath, which is not loaded by the initiator class loader. The classpath can only be loaded by the system class loader or custom loader. Then the SPI interface implementation classes would not be loaded, so the parent delegate model would not work properly here, and we had to find a way to get the system loader to support loading the SPI implementation classes, hence the context class loader

One could argue the realization of the various manufacturers directly into the corresponding interface classes where the bag is not good, at first glance to do so is to solve the problem, but you should know is that no matter in design patterns or JDK is an extension and closed for modification, not only violates the design pattern will make JDK package must be big

  • What does the context classloader do?

It changes the way the parent loader loads, breaking the parent delegate model, by allowing the parent loader to use the currentThread’s thread.currentthread ().getcontextclassloader () ‘class loader to get the loader that loads the classpath class. Using this loader to load classes changes the situation where the parent loader cannot use classes loaded by the child loader

Delegation model order according to one’s parents, the parent class loader to load the will to subclass loader, so it is natural to see and unable to load the subclass class loader loads, the wisdom of the JDK developers found this, think of the class loader in one thread, you can through the thread context class loader to parent can access the class loader to load the son, This is equivalent to putting the system classloader in the context classloader of the current thread, so that when the parent loader needs to fetch classes loaded by the subclass loader, it can do so

The implementation of the ThreadLocal class also takes advantage of the independence of each thread to put the required information into the ThreadLocal. The idea is a space-for-time strategy (multiple threads have their own independent ThreadLocal stores, consuming a certain amount of space. But we don’t have to store the information we need in any other way and get it, which is a huge optimization in time.)

The source document reads:

If not set, the default is the ClassLoader context of the parent Thread. The context ClassLoader of the primordial thread is typically set to the class loader used to load the application.Copy the code

Tells us that if the context class loader is not set, the default value is the class loader that loads the current thread. The class loader that loads the current thread is the class loader that loads the application, usually the system class loader

We’ll take a look at some source code analysis and case studies to see just how powerful the following classloader is to break the parent delegate model

SPI loads and breaks the parent delegate model

SPI – Service Provider Interface, the Service provides the Interface, such as JDBC loading uses SPI, Service providers use SPI extended Interface function, similar to one Interface provided by JDK, different Service providers implement different Interface implementation, encapsulation into a JAR package, By importing this JAR package, we can implement the corresponding functionality using the different interfaces provided by the service provider, and load the implementation of the different service providers through the ServiceLoader class — which you can simply think of as the policy pattern

ServiceLoader

It is a device that loads the service implementation provided by the service provider

A simple service-provider loading facility.Copy the code

Use: The official document reads:

A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services. The file's name is the fully-qualified binary name of the service's type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line. Copy the code

The service provider needs to write a configuration file in the resource directory where the service implementation is located. The file directory is meta-INF /services, and the file name is the fully qualified name of the service type. The file contents should hold the fully qualified name of the service interface implementation class, that is, the package name of the class in the JAR package + the class name

JDBC-> file name: java.sql.Driver File content: com.mysql.cj.jdbc.driver

The JDK will find the java.sql.Driver interface and then find the corresponding com.mysql.cj.jdbc.driver class in the jar package in the file contents as the implementation of the interface

Different providers of the same service will write compliant implementation classes according to the JDK SPI specification (there is no requirement for the class, just implement the interface, but add meta-INF /services/ service qualified name file in each line to write the corresponding fully qualified name of the class provided by the service provider in the jar package directory).

Customize SPI services

Let’s implement an SPI service to see how it works, and then we’ll look at the source code

  • First we write a service interface with a fully qualified name for the interface package pathtop.dzou.jvm.spi
package top.dzou.jvm.spi;

public interface TestInterface {
    void saySomething();
}
Copy the code

  • Then write two different interface service implementations to simulate different implementations provided by different service providers. The package path istop.dzou.jvm.spi.impl
package top.dzou.jvm.spi.impl;
public class ConcreteImpl1 implements TestInterface {
    @Override
    public void saySomething() {
        System.out.println("I am first service provider interface impl;");
    }
}Copy the code

package top.dzou.jvm.spi.impl;
public class ConcreteImpl2 implements TestInterface {
    @Override
    public void saySomething() {
        System.out.println("I am second service provider interface impl;");
    }
}Copy the code

  • We also need to write the configuration file and create the configuration file directory in the classpathMETA-INF/services, configuration file named interface package path fully qualified name. Top dzou. JVM. Spi. TestInterface `
top.dzou.jvm.spi.impl.ConcreteImpl1
top.dzou.jvm.spi.impl.ConcreteImpl2Copy the code

  • Write a test class usingServiceLoader
public class TestSpi { public static void main(String[] args) { //Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader().getParent()); ServiceLoader<TestInterface> loader = ServiceLoader.load(TestInterface.class); Iterator<TestInterface> iterator = loader.iterator(); System.out.println("current class loaded by :"+TestSpi.class.getClassLoader()); System.out.println("current thread loader :"+Thread.currentThread().getContextClassLoader()); System.out.println("service interface loader :"+loader.getClass().getClassLoader()); while(iterator.hasNext()){ TestInterface next = iterator.next(); next.saySomething(); }}} output:  current class loaded by :sun.misc.Launcher$AppClassLoader@18b4aac2 current thread loader :sun.misc.Launcher$AppClassLoader@18b4aac2 service interface loader :null I am first service provider interface impl; I am second service provider interface impl;Copy the code

If we add a line before the first line of main

Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader().getParent());Copy the code

The output is

current class loaded by :sun.misc.Launcher$AppClassLoader@18b4aac2
current thread loader :sun.misc.Launcher$ExtClassLoader@266474c2
service interface loader :nullCopy the code

Explanation:

You can think of the interface implementation we wrote as a jar class written by a service provider, as a service interface provided by the JDK, and then write a configuration file in meta-INF /services in the RESOURCE directory of the JAR with the same fully qualified name as the JDK service interface. If the ServiceLoader class is loaded by the launcher class loader, the thread and test classes are loaded by the system class loader. If the ServiceLoader class is loaded by the launcher class loader, the thread and test classes are loaded by the system class loader.

But when we set the extension class as the thread text class loader, we can see that the print result is that our own service interface implementation is not loaded, so why is this?

A: Is very simple, because the ServiceLoader is through the context class loader retrieves a reference to the system class loader, through the system class loader to help us achieve access to the service implementation class, but now our context class loader to extension class loader, apparently extension class loader is loading and can’t access our own writing the service implementation class, So of course there is no information to print, let alone call the method

SPI principle and ServiceLoader source code analysis

Now that we have a rough idea of how SPI works with the context classloader and custom SPI implementation, let’s take a look at its source code

Because some sun source code is not open to the public, so let’s look at the decomcompiled source code, generally understand

  • First, there is this code in ServiceLoader
private static final String PREFIX = "META-INF/services/";Copy the code

Now you can see why service providers write a directory in a JAR in the classpath directory, an absolute path that the system class loader uses to find the service interface implementation classes in the JAR

  • Let’s look again at the serviceloader.load () method of our custom SPI implementation
public static <S> ServiceLoader<S> load(Class<S> var0) { ClassLoader var1 = Thread.currentThread().getContextClassLoader(); // core method return load(var0, var1); }Copy the code

In load the ServiceLoader takes the context classloader and passes it as an argument to the Load method

private ServiceLoader(Class<S> var1, ClassLoader var2) { this.service = (Class)Objects.requireNonNull(var1, "Service interface cannot be null"); this.loader = var2 == null ? ClassLoader.getSystemClassLoader() : var2; this.acc = System.getSecurityManager() ! = null ? AccessController.getContext() : null; this.reload(); }Copy the code

The load method returns a ServiceLoader object, and the constructor sets the Loader to the current thread context classloader it just grabbed

  • Let’s look at usageloaderThe place where

ServiceLoader maintains an inner class LazyIterator that implements the Iterator interface as an Iterator for all service implementation classes written in the configuration file using the service provider. If you look at the hasNextService method, I’ve left out the key parts

Private Boolean hasNextService() {private Boolean hasNextService() { If (this.configs == null) {try {String var1 = "meta-INF /services/" + this.service.getName(); / / and the service here is the if (this. Loader = = null) {enclosing configs. = this getSystemResources (var1); } else {this.configs = this.loader.getResources(var1); // Use the system class loader to obtain resources from the path in the JAR package. }} Catch (IOException var2) {Serviceloader. fail(this.service, "Error Locating Configuration files", var2); } / / the following use iterators, responsible for judging whether there are other service implementation while (enclosing pending = = null | |! this.pending.hasNext()) { if (! this.configs.hasMoreElements()) { return false; } this.pending = ServiceLoader.this.parse(this.service, (URL)this.configs.nextElement()); } this.nextName = (String)this.pending.next(); return true; }}Copy the code

Look again at the nextService() method

private S nextService() { String var1 = this.nextName; NextName = null; this.nextName = null; Class var2 = null; try { var2 = Class.forName(var1, false, this.loader); Objectvar3 = this.service.cast(var2.newinstance ()); ServiceLoader.this.providers.put(var1, var3); Return var3; }}Copy the code

  • If we look at the initialization method in the most basic Launcher, we know that the Launcher is responsible for loading the class loader, which is the main Launcher class for your application

There’s a code that looks like this

try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        Thread.currentThread().setContextClassLoader(this.loader);Copy the code

It starts by getting the system classloader as a loader reference to save in the Launcher, since it is the bottom classloader in the JDK. The book loader can be obtained by using the getParent method. The thread.currentthread ().setcontextclassloader method is called to set the system classloader to the context classloader for the currentThread

Now let’s take a look at SPI’s use of the service interface in action

SPI – JDBC load analysis

Class. ForName (” com.mysql.cj.jdbc.driver “); Load the mysql driver under the classpath using the same loader that loads the current class (that is, the system class loader)

Now we can look at the image and make it easier to understand. The content of the configuration file, as you probably already know, is the mysql driver for JDBC

Com. Mysql. Cj, JDBC Driver or com. Mysql.. JDBC Driver

  • Let’s take a look at the mysql Driver class
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!" ); }}}Copy the code

When the Driver is loaded via class.forname, the Class is automatically initialized, the static block is executed, and the referenced DriverManger is loaded. According to the parent delegate model, the task of loading DriverManager is handed over to the initiator Class loader

  • The static block of DriverManager initializes the following static block of DriverManager (registerDriver)
static {
        loadInitialDrivers();
    }Copy the code

  • loadInitialDrivers

Let’s look at the method that initializes the Driver executed in its static block

private static void loadInitialDrivers() { String var0 = (String)AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("jdbc.drivers"); }}); AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ServiceLoader var1 = ServiceLoader.load(Driver.class); Java.sql.Driver Iterator var2 = var1. Iterator (); While (var2.hasnext ()) {// Use hasNext to call the hasNextService method to get the resource of the class specified in the configuration file. // Calling the nextService method loads the Class with class.forname ()}} return null; }}); if (var0 ! = null && ! Var0.equals ("")) {// If system.getProperty ("jdbc.drivers"); String[] var1 = var0.split(":"); String[] var2 = var1; int var3 = var1.length; for(int var4 = 0; var4 < var3; ++var4) { String var5 = var2[var4]; println("DriverManager.Initialize: loading " + var5); Class.forName(var5, true, ClassLoader.getSystemClassLoader()); // Try to load the driver in system.getProperty}}}Copy the code

Class. ForName (” com.mysql.cj.jdbc.driver “); ? Is it possible not to call this step manually?

The answer is yes, we’re calling this step manually because the JDK didn’t support it before, so we need to call it, but later versions of the JDK don’t need to call it, because as long as you’re in your classpath, It’s going to call this class.forname () in the loadInitialDrivers call in the next call nextService method

  • Once the driver is loaded, let’s take a look at its methods for getting connections, as well as the class-loading process

String var0: fully qualified name of the driver class

Properties VAR1: contains configuration information about database connection parameters

Class var2: Reflection gets the Class that calls the getConnetion method

The key code is as follows

private static Connection getConnection(String var0, Properties var1, Class<? > var2) throws SQLException { ClassLoader var3 = var2 ! = null ? var2.getClassLoader() : null; Class var4 = DriverManager. Class var4 = driverManager.class; synchronized(DriverManager.class) { if (var3 == null) { var3 = Thread.currentThread().getContextClassLoader(); / / if not the system class loader is set to the current thread 1 class loader, namely storage system class loader reference}} Iterator var5 = registeredDrivers. The Iterator (); While (true) {while(var5.hasNext()) {// Call iterator to load DriverInfo var6 = (DriverInfo)var5.next(); If (isDriverAllowed(var6.driver, var3)) {Connection var7 = var6.driver.connect(var0, var1); if (var7 ! = null) { return var7; } } } } }Copy the code

  • IsDriverAllowed method

It is used to tell whether the driver var0 is loaded by var1(the class loader of the current thread, the class loader of the current calling class), i.e. whether var0 is in the namespace of the class loader of var1

Reasons for this:

1. The context class loader is set to be a high-level class loader rather than a system class loader

2. The thread has been switched, and the context classloader for the current thread is not the classloader that loads the calling class

Different class loaders correspond to different namespaces, so that the classloader referenced by the context classloader cannot load the driver and therefore cannot use it

private static boolean isDriverAllowed(Driver var0, ClassLoader var1) { boolean var2 = false; if (var0 ! = null) { Class var3 = null; try { var3 = Class.forName(var0.getClass().getName(), true, var1); } catch (Exception var5) { var2 = false; } var2 = var3 == var0.getClass(); } return var2; }Copy the code

Brief analysis of Tomcat loading

Web server loading requirements

  • Java libraries used by two Web applications deployed on the same server are isolated from each other, and two different applications can also rely on different versions of a third party library, so a library can only be visible in one application
  • Two Web applications deployed on the same server can share Java class libraries, and 10 rely on Spring, so 10 applications need a separate Spring? Obviously not
  • For security, the libraries used by the server should be isolated from the application libraries
  • Files such as JSP need to support dynamic hot update, JSP changes do not need to restart the server, just need to refresh the page can be

Tomcat loading model

Let’s think about whether the parental delegation model can be implemented in this case.

Obviously not, so Tomcat created its own set of loading models as follows:

  1. Common class loaderLoad libraries that can be shared by both the server and the application, such as the lib directory on the classpath
  2. Catalina class loaderResponsible for loading server-independent libraries that are not shared with the application for security purposes
  3. Shared class loaderIs responsible for loading libraries shared between applications, such as Spring
  4. WebApp class loaderLoad a single application independent class library, invisible to other applications, such as the webApp subclass library
  5. JSP class loaderResponsible for loading JSP files into servlet classes, it needs to be resolvedHot updateThe problem of

Hot update loading of JSP files

When you create a JSP page and start the server, the loader loads it as a servlet-class bytecode file. However, when you modify the JSP content, the class file has been modified. At this point, you have to restart the application to load the class again to implement the update. But if that were the case, no one would use JSP

Tomcat took this into account and proposed an implementation method of one JSP file for each classloader

We create a specific loader for each JSP file load, and each JSP has a Class loader. When we find that the JSP has been modified at run time, we discard the loaded Class file and load the updated JSP file by creating a new JSP Class loader

In order to realize the different application isolation, server and application isolation, is different in the use of parents delegation model, it will give all the load to the parent class, and ensure that each class and only by one, so the tomcat had to destroy the parents entrust model, but it just didn’t follow to the regulation of the upper load, load or top-down model

Tomcat decides to load classes in the WebApp directory by its own WebappClassLoader, without delegating to the parent class loader, and then implements the parent’s access and visibility to classes loaded by the subclass loader through the fraudulent context class loader