1、ClassLoader作用

  • 类加载流程的"加载"阶段是由类加载器完成的。

2、类加载器结构

结构:BootstrapClassLoader(祖父)-->ExtClassLoader(爷爷)-->AppClassLoader(也称为SystemClassLoader)(爸爸)-->自定义类加载器(儿子)

关系:看括号中的排位;彼此相邻的两个为父子关系,前为父,后为子

2.1、BootstrapClassLoader

  • 下边简称为boot
  • C++编写
  • 为ExtClassLoader的父类,但是通过ExtClassLoader的getParent()获取到的是null(在类加载器部分:null就是指boot)
  • 主要加载:E:\Java\jdk1.6\jre\lib\*.jar(最重要的就是:rt.jar)

2.2、ExtClassLoader:

  • 下边简称为ext
  • java编写,位于sun.misc包下,该包在你导入源代码的时候是没有的,需要重新去下
  • 主要加载:E:\Java\jdk1.6\jre\lib\ext\*.jar(eg.dnsns.jar)

2.3、AppClassLoader:

  • 下边简称为app
  • java编写,位于sun.misc包下
  • 主要加载:类路径下的jar

2.4、自定义类加载器:

  • 下边简称为custom
  • 自己编写的类加载器,需要继承ClassLoader类或URLClassLoader,并至少重写其中的findClass(String name)方法,若想打破双亲委托机制,需要重写loadClass方法
  • 主要加载:自己指定路径的class文件

3、全盘负责机制

概念:假设ClassLoaderA要加载class B,但是B引用了class C,那么ClassLoaderA先要加载C,再加载B,"全盘"的意思就是,加载B的类加载器A,也会加载B所引用的类

4、双亲委托机制

这也是类加载器加载一个类的整个过程。

过程:假设我现在从类路径下加载一个类A,

1)那么app会先查找是否加载过A,若有,直接返回;

2)若没有,去ext检查是否加载过A,若有,直接返回;

3)若没有,去boot检查是否加载过A,若有,直接返回;

4)若没有,那就boot加载,若在E:\Java\jdk1.6\jre\lib\*.jar下找到了指定名称的类,则加载,结束;

5)若没找到,boot加载失败;

6)ext开始加载,若在E:\Java\jdk1.6\jre\lib\ext\*.jar下找到了指定名称的类,则加载,结束;

7)若没找到,ext加载失败;

8)app加载,若在类路径下找到了指定名称的类,则加载,结束;

9)若没有找到,抛出异常ClassNotFoundException

注意:

  • 在上述过程中的1)2)3)4)6)8)后边,都要去判断是否需要进行"解析"过程 ("解析"见 第四章 类加载机制
  • 类的加载过程只有向上的双亲委托,没有向下的查询和加载,假设是ext在E:\Java\jdk1.6\jre\lib\ext\*.jar下加载一个类,那么整个查询与加载的过程与app无关。
  • 假设A加载成功了,那么该类就会缓存在当前的类加载器实例对象C中,key是(A,C)(其中A是类的全类名,C是加载A的类加载器对象实例),value是对应的java.lang.Class对象
  • 上述的1)2)3)都是从相应的类加载器实例对象的缓存中进行查找
  • 进行缓存的目的是为了同一个类不被加载两次
  • 使用(A,C)做key是为了隔离类,假设现在有一个类加载器B也加载了A,key为(A,B),则这两个A是不同的A。这种情况怎么发生呢?
    • 假设有custom1、custom2两个自定义类加载器,他们是兄弟关系,同时加载A,这就是有可能的了

总结:

  • 从底向上检查是否加载过指定名称的类;从顶向下加载该类。(在其中任何一个步骤成功之后,都会中止类加载过程)
  • 双亲委托的好处:假设自己编写了一个java.lang.Object类,编译后置于类路径下,此时在系统中就有两个Object类,一个是rt.jar的,一个是类路径下的,在类加载的过程中,当要按照全类名去加载Object类时,根据双亲委托,boot会加载rt.jar下的Object类,这是方法结束,即类路径下的Object类就没有加载了。这样保证了系统中类不混乱。

5、源代码

/**
* 根据指定的binary name加载class。
* 步驟:
* 假设我现在从类路径下加载一个类A,
* 1)那么app会先查找是否加载过A(findLoadedClass(name)),若有,直接返回;
* 2)若没有,去ext检查是否加载过A(parent.loadClass(name, false)),若有,直接返回;
* findBootstrapClassOrNull(name) 3)4)5)都是这个方法
* 3)若没有,去boot检查是否加载过A,若有,直接返回;
* 4)若没有,那就boot加载,若在E:\Java\jdk1.6\jre\lib\*.jar下找到了指定名称的类,则加载,结束;
* 5)若没找到,boot加载失败;
* findClass(name) 6)7)8)9)都是这个方法
* 在findClass中调用了defineClass方法,该方法会生成当前类的java.lang.Class对象
* 6)ext开始加载,若在E:\Java\jdk1.6\jre\lib\ext\*.jar下找到了指定名称的类,则加载,结束;
* 7)若没找到,ext加载失败;
* 8)app加载,若在类路径下找到了指定名称的类,则加载,结束;
* 9)若没有找到,抛出异常ClassNotFoundException
* 注意:在上述过程中的1)2)3)4)6)8)后边,都要去判断是否需要进行"解析"过程
*/
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class c = findLoadedClass(name);//检查要加载的类是不是已经被加载了
if (c == null) {//没有被加载过
try {
if (parent != null) {
//如果父加载器不是boot,递归调用loadClass(name, false)
c = parent.loadClass(name, false);
} else {//父加载器是boot
/*
* 返回一个由boot加载过的类;3)
* 若没有,就去试着在E:\Java\jdk1.6\jre\lib\*.jar下查找 4)
* 若在bootstrap class loader的查找范围内没有查找到该类,则返回null 5)
*/
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//父类加载器无法完成加载请求
}
if (c == null) {
//如果父类加载器未找到,再调用本身(这个本身包括ext和app)的findClass(name)来查找类
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}

说明:

  • 该段代码中引用的大部分方法实质上都是native方法
  • 其中findClass方法的类定义如下:
    /**
    * 查找指定binary name的类
    * 该类应该被ClassLoader的实现类重写
    */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
    }
    • 关于findClass可以查看URLClassLoader.findClass(final String name),其中引用了defineClass方法,在该方法中将二进制字节流转换为了java.lang.Class对象。

    附:关于递归

    递归基于栈实现。

    上述的代码如果不清楚递归的意义是看不清的。

    解释:

    • app_loadClass()方法执行到ext_loadClass(),这时候对于app_loadClass()中剩余的findClass()会在栈中向下压;
    • 然后执行ext_loadClass(),当执行到findBootstrapClassOrNull(name),这时候ext_loadClass()中剩余的findClass()也会从栈顶向下压,此时ext_loadClass()_findClass()仅仅位于app_loadClass()_findClass()的上方;
    • 然后执行findBootstrapClassOrNull(name),当boot检测过后并且执行完加载后并且没成功,boot方法离开栈顶;
    • 然后执行此时栈顶的ext_loadClass()_findClass()
    • 然后执行此时栈顶的app_loadClass()_findClass()

    这样,就完成了双亲委托机制。

    递归太烦了,实际开发中尽量不要用!

类加载器ClassLoader源码解析的更多相关文章

  1. 第五章 类加载器ClassLoader源码解析

    说明:了解ClassLoader前,先了解 第四章 类加载机制 1.ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的. 2.类加载器结构 结构:Bootstr ...

  2. JVM 类加载器ClassLoader源码学习笔记

    类加载 在Java代码中,类型的加载.连接与初始化过程都是在程序运行期间完成的. 类型可以是Class,Interface, 枚举等. Java虚拟机与程序的生命周期 在如下几种情况下,Java虚拟机 ...

  3. DRF-解析器组件源码解析

    解析器组件源码解析 解析器组件源码解析 1 执行request.data 开始找重装的request中的data方法 2 在dispatch找到重装的request def dispatch(self ...

  4. 1.1 jvm核心类加载器--jdk源码剖析

    目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...

  5. Flink Sql 之 Calcite Volcano优化器(源码解析)

    Calcite作为大数据领域最常用的SQL解析引擎,支持Flink , hive,  kylin , druid等大型项目的sql解析 同时想要深入研究Flink sql源码的话calcite也是必备 ...

  6. spark内存管理器--MemoryManager源码解析

    MemoryManager内存管理器 内存管理器可以说是spark内核中最重要的基础模块之一,shuffle时的排序,rdd缓存,展开内存,广播变量,Task运行结果的存储等等,凡是需要使用内存的地方 ...

  7. [源码解析] PyTorch分布式优化器(2)----数据并行优化器

    [源码解析] PyTorch分布式优化器(2)----数据并行优化器 目录 [源码解析] PyTorch分布式优化器(2)----数据并行优化器 0x00 摘要 0x01 前文回顾 0x02 DP 之 ...

  8. 类加载器 - ClassLoader详解

    获得ClassLoader的途径 获得当前类的ClassLoader clazz.getClassLoader() 获得当前线程上下文的ClassLoader Thread.currentThread ...

  9. Mybatis源码解析,一步一步从浅入深(三):实例化xml配置解析器(XMLConfigBuilder)

    在上一篇文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码 ,中我们看到 代码:XMLConfigBuilder parser = new XMLConfigBuilder(read ...

随机推荐

  1. iOS 开发,相关网址

    iOS 开发,相关网址 说明 网址 注册开发者 https://developer.apple.com/cn/programs/enroll/ 未付费688个人开发账号真机调试测试教程 http:// ...

  2. 将公式直接转化为Latex代码的神器-snip

    经常用latex写论文,免不了要敲各种公式,今天和大家分享一个神器-snip,它可以直接将公式转化为代码,不需要我们自己编写公式代码,方便快捷,准确率极高.该神器的下载地址为:https://math ...

  3. 浏览器性能监控performance使用

    浏览器中有一个performance的性能监控,平时我也没有用到,接手了一个大数据的项目,发现页面打开的比较慢,使用浏览器的performance分析可以看到各个步骤花费的时间. 关于项目的性能分析如 ...

  4. stm32中的型号对比——为什么很少用STM32F2,F3?

    源自网络 我觉得有三点: 1. F2属于加强版的F1,内核还是cortex M3,只是主频提高到了120MHz(F1是72MHz),但是这点提升没有实质性意义,性能比不上 2. F3是F4的削弱版,一 ...

  5. 02_搭建HTTP客户端与服务器(数通华为)

    1.选择客户端和服务端: 2.服务端创建HTTP目录: 3.客户端访问下载:

  6. vue分组全选权限,CheckBoxGroup

    <template> <div class="comPower"> <div class="card_header" v-show ...

  7. clr调试扩展和DAC

    SOS.DLL.SOSEX.DLL这两个就是用来对.NET程序在Windows调试工具中起到翻译作用的调试器扩展.简单讲就是,这两个组件是.NET项目组专门开发出来用来对.NET应用程序进行方便调试用 ...

  8. ent 基本使用 三 边(关系处理)

    ent 提供了图查询的能力,实际上在关系数据库中的表现就是relation,以下代码接前文 添加边(关系) 添加schema entc init Car Group 效果: 添加字段 car pack ...

  9. cf1175 D\E

    链接 成功带wxy掉分..全程0输出 D E D 题意 把序列分成连续k段,f(i)表示i这个在第几段 \(\sum\limits_{i=1}^{n}a_i*f(i)\)最大 思路 想象成从k层积木依 ...

  10. 【数论】[因数个数]P4167樱花

    题目描述 求不定方程 \(\frac {1}{x} + \frac{1}{y} = \frac{1}{n!}\)的正整数解的个数 \(n \leq 100^6\) Solution 化简得 \(x * ...