百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

什么,JVM自己破坏了双亲委派模型?

nanshan 2024-11-10 10:10 7 浏览 0 评论

说是双亲,其实多级单亲,无奈迎合历史的错误翻译吧。

1 工作流程

  • 当一个类加载器收到一个类加载请求 在 JDK9 后,会首先搜索它的内建加载器定义的所有“具名模块”:如果找到合适的模块定义,将会使用该加载器来加载如果未找到,则会将该请求委派给父级加载器去加载
  • 因此所有的类加载请求最终都应该被传入到启动类加载器(Bootstrap ClassLoader)中,只有当父级加载器反馈无法完成这个列的加载请求时(它的搜索范围内不存在这个类),子级加载器才尝试加载。
  • 在类路径下找到的类将成为这些加载器的无名模块。

    这里的父子关系是组合而不是继承。

  • 双亲委派模型示意图
  • 双亲委派模型的优点

  • 避免重复加载 父类已经加载了,子类就不需要再次加载。 eg,object 类。它存放在 rt.jar 中,无论哪个类加载器要加载这个类,最终都是委派给处于模型顶端的启动类加载器加载,因此 object 类在程序的各种加载环境中都是同一个类。
  • 更安全 解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心 API,会带来安全隐患。
  • 双亲委派模型的实现

     protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // 首先先检查该类已经被加载过了
                Class c = findLoadedClass(name);
                if (c == null) {//该类没有加载过,交给父类加载
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {//交给父类加载
                            c = parent.loadClass(name, false);
                        } else {//父类不存在,则交给启动类加载器加载
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                       //父类加载器抛出异常,无法完成类加载请求
                    }
    
                    if (c == null) {//
                        long t1 = System.nanoTime();
                        //父类加载器无法完成类加载请求时,调用自身的findClass方法来完成类加载
                        c = findClass(name);
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    
    

    3.3 类加载的方式

    1. 通过命令行启动应用时由JVM初始化加载含有main()方法的主类。
    2. 通过Class.forName()方法动态加载,会默认执行初始化块(static{}),但是Class.forName(name,initialize,loader)中的initialze可指定是否要执行初始化块。
    3. 通过ClassLoader.loadClass()方法动态加载,不会执行初始化块。

    自定义类加载器

    实现方式

  • 遵守双亲委派模型 继承ClassLoader,重写findClass()方法。
  • 破坏双亲委派模型 继承ClassLoader,重写loadClass()方法。
  • 通常我们推荐采用第一种方法自定义类加载器,最大程度上的遵守双亲委派模型。

    如果有一个类加载器能加载某个类,称为定义类加载器,所有能成功返回该类的Class的类加载器都被称为初始类加载器。

    自定义类加载的目的是想要手动控制类的加载,那除了通过自定义的类加载器来手动加载类这种方式,还有其他的方式么?

    利用现成的类加载器进行加载:

    1. 利用当前类加载器 Class.forName(); 2. 通过系统类加载器 Classloader.getSystemClassLoader().loadClass(); 3. 通过上下文类加载器 Thread.currentThread().getContextClassLoader().loadClass();


    l 利用URLClassLoader进行加载:

    URLClassLoader loader=new URLClassLoader(); loader.loadClass();



    类加载实例演示: 命令行下执行HelloWorld.java

    public class HelloWorld{
        public static void main(String[] args){
            System.out.println("Hello world");
        }
    }
    

    该段代码大体经过了一下步骤:

    1. 寻找jre目录,寻找jvm.dll,并初始化JVM.
    2. 产生一个Bootstrap ClassLoader;
    3. Bootstrap ClassLoader加载器会加载他指定路径下的java核心api,并且生成Extended ClassLoader加载器的实例,然后Extended ClassLoader会加载指定路径下的扩展java api,并将其父设置为Bootstrap ClassLoader。
    4. Bootstrap ClassLoader生成Application ClassLoader,并将其父Loader设置为Extended ClassLoader。
    5. 最后由AppClass ClassLoader加载classpath目录下定义的类——HelloWorld类。

    我们上面谈到 Extended ClassLoader和Application ClassLoader是通过Launcher来创建,现在我们再看看源代码:

     public Launcher() {
            Launcher.ExtClassLoader var1;
            try {
                //实例化ExtClassLoader
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }
    
            try {
                //实例化AppClassLoader
                this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }
            //主线程设置默认的Context ClassLoader为AppClassLoader.
            //因此在主线程中创建的子线程的Context ClassLoader 也是AppClassLoader
            Thread.currentThread().setContextClassLoader(this.loader);
            String var2 = System.getProperty("java.security.manager");
            if(var2 != null) {
                SecurityManager var3 = null;
                if(!"".equals(var2) && !"default".equals(var2)) {
                    try {
                        var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                    } catch (IllegalAccessException var5) {
                        ;
                    } catch (InstantiationException var6) {
                        ;
                    } catch (ClassNotFoundException var7) {
                        ;
                    } catch (ClassCastException var8) {
                        ;
                    }
                } else {
                    var3 = new SecurityManager();
                }
    
                if(var3 == null) {
                    throw new InternalError("Could not create SecurityManager: " + var2);
                }
    
                System.setSecurityManager(var3);
            }
    
        }
    

    破坏双亲委派模型

    双亲模型有个问题:父加载器无法向下识别子加载器加载的资源。

  • 如下证明 JDBC 是启动类加载器加载,但 mysql 驱动应用类加载器。而 JDBC 运行时又需要去访问子类加载器加载的驱动,就破坏了该模型。 JDK 自己为解决该问题,引入线程上下问类加载器,可以通过Thread的setContextClassLoader()进行设置
  • 当为启动类加载器时,使用当前实际加载驱动类的类加载器
  • 热替换

    比如OSGI的模块化热部署,它的类加载器就不再是严格按照双亲委派模型,很多 可能就在平级的类加载器中执行了。

    FAQ

    1. ClassLoader通过一个类全限定名来获取二进制流,如果我们需通过自定义类加载其来加载一个Jar包的时候,难道要自己遍历jar中的类,然后依次通过ClassLoader进行加载吗?或者说我们怎么来加载一个jar包呢? 对于动态加载jar而言,JVM默认会使用第一次加载该jar中指定类的类加载器作为默认的ClassLoader。

    假设我们现在存在名为sbbic的jar包,该包中存在ClassA和ClassB类(ClassA中没有引用ClassB)。 现在我们通过自定义的ClassLoaderA来加载在ClassA这个类,此时ClassLoaderA就成为sbbic.jar中其他类的默认类加载器。即ClassB默认也会通过ClassLoaderA去加载。

    1. 如果一个类引用的其他的类,那么这个其他的类由谁来加载?

    如果ClassA中引用了ClassB呢? 当类加载器在加载ClassA的时候,发现引用了ClassB,此时类加载如果检测到ClassB还没有被加载,则先回去加载。当ClassB加载完成后,继续回来加载ClassA。即类会通过自身对应的来加载其加载其他引用的类。

    1. 既然类可以由不同的加载器加载,那么如何确定两个类如何是同一个类?

    JVM规定:对于任何一个类,都需要由加载它的类加载器和这个类本身一同确立在java虚拟机中的唯一性。即在jvm中判断两个类是否是同一个类取决于类加载和类本身,也就是同一个类加载器加载的同一份Class文件生成的Class对象才是相同的,类加载器不同,那么这两个类一定不相同。

    相关推荐

    python获取阿里云云解析dns的域名解析记录

    最近由于工作原因接触到阿里云的服务,我需要实时获取所有的域名信息,用于对其进行扫描,因此写了一个自动化爬取脚本给需要的人分享。(阿里云有官方的demo,有兴趣的可以自己看一下,后面也会放链接,我只能...

    前端性能优化系列——DNS预解析和优化

    简单来说,DNS的作用是将域名解析为IP地址,解析的过程是耗时的,转化后会做本地缓存,我们的优化的目标主要是针对用户第一次访问站点的时候陷入长时间白屏的问题。DNS解析可以分为两类,第一类是页...

    dns错误修复方法

      最近用户反馈在同一网络的其他电脑可以正常上网,但自己的电脑却提示页面找不到且无法解析服务器的dns地址,接下来给大家带来dns错误修复方法。  1、点击网络图标,选择打开网络共享中心,如图所示: ...

    技术分享 | 浅谈DNS递归解析和迭代解析之间的区别

    DNS解析是互联网中的重要环节,承担着将域名翻译为可由计算机直接读取的IP地址的基础功能。根据查询对象不同DNS解析可分为递归解析和迭代解析两种方式,接下来,中科三方将简单介绍下两种查询方式的流程以及...

    一文读懂DNS解析故障常见情况(中科三方)

    DNS解析将人们习惯使用的域名翻译成计算机识别的IP地址,是确保人们正常访问网站的重要功能。而在实际域名管理过程中,经常会因为种种原因导致DNS解析故障。DNS解析故障主要表现在人们通过IP地址可以直...

    DNS分离解析实验

    如果本文对你有帮助,欢迎关注、点赞、收藏、转发给朋友,让我有持续创作的动力目录一、分离解析概述二、实验需求三、实验步骤3.1双网卡服务器配置3.1.1添加两张网卡(内外网)3.1.2对两个网卡进...

    #净网2019# 浏览网页被“劫持”,有问题!

    明明自己没有设置过,打开网页浏览器却直接到了一个陌生网站,想改回原来的主页设置颇费周折、甚至无能为力。很多网民有过类似经历:在安装了一些软件后,自己的浏览器主页就被修改和锁定。本来打算访问A网站却被强...

    解决浏览器劫持,360和腾讯安全不敌火绒专杀

    上午win7莫名其妙显示未激活,并要求当日必须激活,没办法,上网找激活工具,先试了“小马”没起作用,又下载了“WIN7ActivationV2.3绿色版”,就是这个:激活是激活了,顺便给我安了一堆垃...

    【净网2019】 浏览网页被“劫持”,有问题!

    明明自己没有设置过,打开网页浏览器却直接到了一个陌生网站,想改回原来的主页设置颇费周折、甚至无能为力。很多网民有过类似经历:在安装了一些软件后,自己的浏览器主页就被修改和锁定。本来打算访问A网站却被...

    浏览器打开网页被绑架?这里有办法

    最近小A遇到了一个小问题:在240g上网冲浪的时候,搜索到的网页,点进去却“李逵变李鬼”???在使用浏览器时,点击搜寻结果却导向与搜寻内容不符合的网站,表示您的浏览器可能已遭受恶意软件劫持。例如您在搜...

    处理浏览器主页被劫持的最新方法

    给大家说个处理浏览器主页被劫持的最新方法:刚遇件让人哭笑不得的事,这两天发现浏览器的主页打开后不是自己原来设置的,而是hao123,一百度,好家伙,原来这个流氓许多人都遇到过,照着大家分享的方法一一尝...

    软件性能测试详解

    性能测试的基本概念性能测试是一种非功能性测试,通过自动化工具模拟多种负载条件(正常、峰值、异常),对系统的各项性能指标进行测试和评估,以验证其是否满足预期的性能需求。以下是核心概念的详细解析:一、性能...

    国内粉色图标视频网站偷用带宽风波,WebRTC Control 插件来救场

    最近,B站陷入了一场舆论风波,被指偷偷使用用户上传带宽。有网友在浙江大学论坛投稿称,B崭新版App疑似未经许可大量上传数据致网络卡顿,查看路由器统计信息后,发现B站手机客户端开启大量端口,...

    服务器知识

    问:机架式和非机架式是什么意思?服务器中“U”是什么单位?答:机架式、非机架式指的是服务器的类型。机架式是指可以直接插入机柜的标准服务器。非机架式是其他类型的服务器。譬如说我们普通的pc机的主机。大小...

    Vue3 性能拉胯?5 个实战技巧让项目响应速度飙升 60%!

    作为前端工程师,在开发Vue3项目时,你是否经常遇到页面卡顿、数据更新缓慢,导致用户体验直线下降的情况?看着自己精心搭建的应用,因为性能问题被吐槽,真是既无奈又焦虑。别担心,今天就分享5个超级...

    取消回复欢迎 发表评论: