当前位置: 首页 > news >正文

类加载器及其委托机制的深入分析

Java虚拟机可以安装多个类加载器,系统默认三个主要的类加载器,每个加载器负责加载特定位置的类:
BootStrap,ExtClassLoader,AppClassLoader。

类加载器本身也是一个Java类,因为其他Java类的类加载器本身也要被类加载器加载,所以肯定有一个类加载器不是Java类,这便是BootStrap,BootStrap嵌套在JVM内核中。Java虚拟机中所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载器。

当Java虚拟机要加载一个类时,首先由当前线程的类加载器去加载线程中的第一个类,如果类A引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B,当然,也可以通过直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载 类时,又先委托给其上级类加载器,当所有祖宗类加载器都没有加载到类,则会回到发起者类加载器,如果还加载不了,则抛出ClassNotFoundException,而不会去寻找发起者的儿子去加载,因为没有getChild()方法。看图:

这里写图片描述

当Java虚拟机要加载一个类时,一般来说,一个加载请求由AppClassLoader发起,但是AppClassLoader并不会尝试去加载该类,而是把这个任务委托给他的父类,即ExtClassLoader,而ExtClassLoader也不会去加载,而是又委托给他的父类,即BootStrap,BootStrap已经是终极大boss了,他不能委托给其他人,只好自己去寻找class文件,如果他找到了class文件,则将之加载,当他找不到时,又把任务推给儿子,也就是ExtClassLoader,ExtClassLoader这时才会去寻找,如果他找到了class文件,则将之加载,当他找不到时,又把任务推给儿子,也就是AppClassLoader,如果他找到了class文件,则将之加载,当他找不到时,则抛出一个ClassNotFoundException,加载请求的发起者不会再去寻找自己的儿子让他加载。
看下面一段代码,相关分析都在注释里:


    public static void main(String[] args) {
        // 先得到类加载器,再拿到类加载器的字节码,最后根据字节码拿到类加载器的名称
        System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
                .getName());
        // System的类加载器为null,说明System的类加载器是BootStrap
        System.out.println(System.class.getClassLoader());
    }

输出:

这里写图片描述

说明目前运行类的类加载器是AppClassLoader,如果我们把当前类打包成一个jar包,放在C:\Program Files\Java\jre1.8.0_31\lib\ext目录下(每个人不一样),我们再尝试运行,看输出:

这里写图片描述

这次变成了ExtClassLoader来加载类了,为什么呢?稍微分析一下就会很清楚,首先是AppClassLoader发起一个加载类的请求,这个请求层层上传,一直传到他的爷爷也就是BootStrap那里,他的爷爷没找到class文件又把请求发回给ExtClassLoader,因为C:\Program Files\Java\jre1.8.0_31\lib\ext文件夹中已经有了我们要加载的类,而这个文件夹又恰好由ExtClassLoader来负责加载,所以ExtClassLoader就把这个类加载了,消息就不再往儿子那里传递了,所以输出的是ExtClassLoader加载了类。

通过下面的代码,我们可以看出类加载器之间的父子关系:

    @Test
    public void test2(){
        //拿到一个类加载器
        ClassLoader loader = ClassLoaderTest.class.getClassLoader();
        //循环依次遍历loader的父类
        while(loader!=null){
            System.out.println(loader.getClass().getName());
            loader = loader.getParent();
        }
        //最后打印出loader的老祖宗,null
        System.out.println(loader);
    }

输出:
这里写图片描述

转载于:https://www.cnblogs.com/lenve/p/4517995.html

相关文章:

  • 《.NET 4.0面向对象编程漫谈》勘误表(2011年1月14日更新)
  • 【转】Android中的内存管理--不错不错,避免使用枚举类型
  • VisualSVN server安装及使用 转
  • Protobuf-net学习笔记
  • how to solve ORA-02293
  • (floyd+补集) poj 3275
  • 我大四了,oh,我要毕业了 _随笔
  • MeeGo handset 1.1开发环境[3]:直接使用Qemugl
  • Resharper
  • C++ VS C#(1):注释,变量,控制台输出
  • 我的java mvc
  • 项目管理学习笔记三:项目管理一般知识
  • Markdown——入门指南
  • 项目管理学习笔记四:项目立项管理
  • 项目管理学习笔记五:项目整体管理
  • JS 中的深拷贝与浅拷贝
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • miaov-React 最佳入门
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Redis字符串类型内部编码剖析
  • Spring-boot 启动时碰到的错误
  • text-decoration与color属性
  • ViewService——一种保证客户端与服务端同步的方法
  • VuePress 静态网站生成
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 前端技术周刊 2019-02-11 Serverless
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 《天龙八部3D》Unity技术方案揭秘
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • #13 yum、编译安装与sed命令的使用
  • #控制台大学课堂点名问题_课堂随机点名
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (Python第六天)文件处理
  • (TOJ2804)Even? Odd?
  • (备忘)Java Map 遍历
  • (分布式缓存)Redis持久化
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (接口封装)
  • (十五)使用Nexus创建Maven私服
  • (一)RocketMQ初步认识
  • (一)u-boot-nand.bin的下载
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (轉)JSON.stringify 语法实例讲解
  • .bat文件调用java类的main方法
  • .gitignore文件_Git:.gitignore
  • .Net 8.0 新的变化
  • .net core Swagger 过滤部分Api
  • .net core控制台应用程序初识
  • .Net 应用中使用dot trace进行性能诊断
  • .NET建议使用的大小写命名原则
  • .net连接MySQL的方法
  • //解决validator验证插件多个name相同只验证第一的问题
  • @EnableAsync和@Async开始异步任务支持