类加载机制
-
JVM自带的加载器包括如下几种
- 启动类加载器(BootStrapClassLoader)
- 用于加载启动的基础模块类,比如
- 平台类加载器(PlatformClassLoader)备注:JDK9之前是扩展类加载器(ExtensionClassLoader)】
- 应用程序类加载器(AppClassLoader)
- 用于加载应用级别的基础模块类和程序员开发的Class
- 启动类加载器(BootStrapClassLoader)
-
用户自定义类加载器
- 自定义类加载器是在所有系统类加载器执行之后才可以执行
-
下图是父子关系而非继承关系【重点,此处容易产生误解】
package com.unicorn.classloader;
/**
* @desc: TODO
* @author: quan
* @date: 2020-07-18 21:24
*/
public class ClassLoaderStudy {
public static void main(String[] args) throws ClassNotFoundException {
//BootStrapClassLoader 由于BootStrapClassLoader是虚拟机的基础模块,不允许业务上做任何操作,因此getClassLoader() 是NULL
String str = "Hello class loader study by BootStrapClassLoader";
System.out.println("str class Loader =" + str.getClass().getClassLoader());
Class driver = Class.forName("java.sql.Driver");
System.out.println("driver class Loader =" + driver.getClassLoader());
//ExtClassLoader
//AppClassLoader APP应用程序ClassLoader
System.out.println("ClassLoaderStudy class Loader =" + ClassLoaderStudy.class.getClassLoader());
System.out.println("ClassLoaderStudy.parent class Loader =" + ClassLoaderStudy.class.getClassLoader().getParent());
System.out.println("ClassLoaderStudy.parent.parent class Loader =" + ClassLoaderStudy.class.getClassLoader().getParent().getParent());
}
}
双亲委派模型
- 双亲委派模型工作过程:(jdk9以下,jdk9开始引入了模块化的概念,此处会有细微的差别)
- 如果一个类加载器接收到了类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委派给父级加载器去完成
- 每一个层次的类加载都是如此,因此所有的类加载请求都应该传送到顶层启动类加载器中,只有当父加载器反馈自己无法加载这个请求,子类加载器才会去尝试自己加载。
- 双亲委派模型核心代码java.lang.ClassLoader#loadClass(java.lang.String, boolean)
/**
* Loads the class with the specified <a href="#name">binary name</a>. The
* default implementation of this method searches for classes in the
* following order:
*
* <ol>
*
* <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
* has already been loaded. </p></li>
*
* <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
* on the parent class loader. If the parent is <tt>null</tt> the class
* loader built-in to the virtual machine is used, instead. </p></li>
*
* <li><p> Invoke the {@link #findClass(String)} method to find the
* class. </p></li>
*
* </ol>
*
* <p> If the class was found using the above steps, and the
* <tt>resolve</tt> flag is true, this method will then invoke the {@link
* #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
*
* <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
* #findClass(String)}, rather than this method. </p>
*
* <p> Unless overridden, this method synchronizes on the result of
* {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
* during the entire class loading process.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @param resolve
* If <tt>true</tt> then resolve the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//TODO 看这里,先通过父级ClassLoader加载器进行查找
if (parent != null) {
c = parent.loadClass(name, false);
} else {
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 stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
破坏双亲委派模型
- 此处仅作了解即可
- 常见的是jdbc驱动加载的问题
- java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>)
- java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>)