「JVM 类加载子系统」系列文章
在正式介绍双亲委派模型之前,要思考两个问题,为什么要研究类加载的过程?为什么要研究双亲委派模型?
研究类加载的过程是为了知道加载过程使用到了双亲委派模型,但仅仅知道使用了双亲委派模型还不够,最终目的是要弄清楚为什么要使用双亲委派模型?!双亲委派模型的原理?!双亲委派模型背后的逻辑思想?!该思想是否可以被我们借鉴,为我所用?!
比如:双亲委派模型避免了类的重复加载,避免了核心类库被修改。那么我们在设计框架时,框架底层的内容要不容易被篡改,或者不被攻击,这个时候就可以借鉴双亲委派模型!!
通过上一部分输出的文件目录可以看出,「应用程序类加载器」加载的文件包含了「启动类加载器」加载的文件和「扩展类加载器」加载的文件,那这是不是意味着重复加载了呢?!
其实不然,根据前文 类加载器 的介绍「应用程序类加载器」主要负责加载用户类路径 (ClassPath) 上所有的类库,也就是对应输出中的../../../../concurrency/target/classes
之所以不会重复加载,完全是因为双亲委派模型,它分为两个过程:
注意:双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。不过这里类加载器之间的父子关系一般不是继承来实现的,而是通常使用组合关系来复用父加载器的代码
工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求 (它的搜索范围中没有找到所需的类) 时,子加载器才会尝试自己去完成加载
1. 可以避免类重复加载
2. 可以防止核心 API 库被随意修改
java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object的类,并放在程序的ClassPath中,那系统中就会出现多个不同的Object类,Java 类型体系中最基础的行为也就无从保证,应用程序将变得一片混乱rt.jar类库中已有类重名的 Java 类,将会发现它可以正常编译,但永远无法被加载运行3. 全盘委托机制
关于三个类加载器的源码分析可见 剖析 [Bootstrap、Extension、Application] ClassLoader
C++ 语言调用了sun.misc.Launcher.getLauncher()获取了 launcher 对象,Launcher 类初始化的时候其构造器创建了 ExtClassLoader 和 AppClassLoader,然后接下来调用 launcher 对象的getClassLoader()方法
xxxxxxxxxxpublic ClassLoader getClassLoader() { return loader;}调用getClassLoader()方法获得了loader对象,而loader对象在构造函数中被赋值
xxxxxxxxxxpublic Launcher() { // Create the extension class loader ClassLoader extcl; try { // 获取扩展类加载器,getExtClassLoader() 见下方 extcl = ExtClassLoader.getExtClassLoader(); } catch (IOException e) { throw new InternalError( "Could not create extension class loader", e); } // Now create the class loader to use to launch the application try { // 获取应用程序类加载器 loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new InternalError( "Could not create application class loader", e); } // 省略其它代码 ...}类加载器通过调用loader.loadClass("com.lfool.My")来加载类,根据上面的分析,可以知道双亲委派模型的起点是AppClassLoader,也正如前文 类加载器 所说:如果应用中没有自定义类加载器,一般情况下使用的就是 AppClassLoader 作为默认类加载器
下图分别是 AppClassLoader 和 ExtClassLoader 的关系图 (IDEA 快捷键:Option + Command + U)
向上委托查找,向下加载
// AppClassLoader 类public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{ int i = name.lastIndexOf('.'); if (i != -1) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPackageAccess(name.substring(0, i)); } } // 如果类已经加载 if (ucp.knownToNotExist(name)) { Class<?> c = findLoadedClass(name); if (c != null) { if (resolve) { resolveClass(c); } return c; } throw new ClassNotFoundException(name); } // 如果类没有被加载,调用父类的 loadClass() 方法 // AppClassLoader 的父类是 URLClassLoader,但是 URLClassLoader 中没有 loadClass() 方法 // 继续往上看,URLClassLoader 的父类是 ClassLoader,有 loadClass() 方法 // 所以 此处调用的是 ClassLoader 中的 loadClass() 方法,具体见下方 return (super.loadClass(name, resolve));}// ClassLoader 类protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{ // synchronized 同步锁,所以类加载是线程安全的 synchronized (getClassLoadingLock(name)) { // 首先,检查请求的类是否已经被加载过了 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { // 父类加载器不为 null,委托给父类加载器加载 c = parent.loadClass(name, false); } else { // 父类加载器为 null,委托给启动类加载器加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果父类加载器抛出 ClassNotFoundException // 说明父类加载器无法完成加载请求 } // 到这一步 c 还为 null,表示父类加载器无法加载该类 if (c == null) { // 在父类加载器无法加载时,再调用本身的 findClass 方法来进行类加载 long t1 = System.nanoTime(); // 由于 ClassLoader 类中 findClass 是个抽象方法,需要回到实现了该方法的类中找,回到 URLClassLoader 中,具体见下方 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; }}// URLClassLoader 类// 根据位置和名称加载 class 字节码protected Class<?> findClass(final String name) throws ClassNotFoundException{ final Class<?> result; try { // doPrivileged 是一个权限校验的操作,可以先不管 result = AccessController.doPrivileged( new PrivilegedExceptionAction<Class<?>>() { public Class<?> run() throws ClassNotFoundException { // com.lfool.My -> com/lfool/My.class,「全限定类名」转化成「类路径」 String path = name.replace('.', '/').concat(".class"); // 去 resource 库中找这个路径,没有就返回 null Resource res = ucp.getResource(path, false); if (res != null) { try { // 找到了,开始执行加载、验证、准备、解析、初始化,具体见下方 return defineClass(name, res); } catch (IOException e) { throw new ClassNotFoundException(name, e); } catch (ClassFormatError e2) { if (res.getDataError() != null) { e2.addSuppressed(res.getDataError()); } throw e2; } } else { return null; } } }, acc); } catch (java.security.PrivilegedActionException pae) { throw (ClassNotFoundException) pae.getException(); } if (result == null) { throw new ClassNotFoundException(name); } return result;}// 将字节码转化为 Class 对象private Class<?> defineClass(String name, Resource res) throws IOException { long t0 = System.nanoTime(); int i = name.lastIndexOf('.'); // 获取 classes 目录的绝对路径,如:file:/Users/lfool/myself/IdeaProjects/concurrency/target/classes URL url = res.getCodeSourceURL(); if (i != -1) { // 获取包名 String pkgname = name.substring(0, i); // Check if package already loaded. Manifest man = res.getManifest(); definePackageInternal(pkgname, man, url); } // 读入 class 的字节流 java.nio.ByteBuffer bb = res.getByteBuffer(); if (bb != null) { // Use (direct) ByteBuffer: CodeSigner[] signers = res.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); return defineClass(name, bb, cs); } else { byte[] b = res.getBytes(); // must read certificates AFTER reading bytes. CodeSigner[] signers = res.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0); // 见下方 return defineClass(name, b, 0, b.length, cs); }}// ClassLoader 类// 通常是一些基础的校验,比如准备阶段,解析阶段,初始化阶段都是本地方法protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError{ // 预定义类信息 protectionDomain = preDefineClass(name, protectionDomain); // 定义类源码 String source = defineClassSourceLocation(protectionDomain); // 初始化类,调用本地方法 Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); // 类定义后置处理 postDefineClass(c, protectionDomain); return c;}