• Java类加载机制


    本文转载自:https://blog.csdn.net/qq_22912803/article/details/78065847

    一、java的classLoader加载机制大家都比较清楚,就是委托机制,如下:

         这种机制就是,加载一个类的时候,会一直向上委托,如果BootStrapClassLoader加载不到,然后再依次往下加载,如果最后SystemClassLoader也加载不到,则会抛出classNotFoundException。

    二、现在我们来说说其他的,说之前先说说两个概念,这个是转述别人的(http://blog.csdn.net/yongche_shi/article/details/39695991)

    1、有两个术语,一个叫“定义类加载器”,一个叫“初始类加载器”。
         比如有如下的类加载器结构:
        BootstrapClassloader
        ExtClassloader
          AppClassloader
          -自定义clsloadr1
            -自定义clsloadr2 
         如果用“自定义clsloadr1”加载java.lang.String类,那么根据双亲委派最终bootstrap会加载此类,那么bootstrap类就叫做该类的“定义类加载器”,而包括bootstrap的所有得到该类class实例的类加载器都叫做“初始类加载器”。

    2、所说的“命名空间”,是指jvm为每个类加载器维护的一个“表”,这个表记录了所有以此类加载器为“初始类加载器”(而不是定义类加载器,所以一个类可以存在于很多的命名空间中)加载的类的列表,所以,题目中的问题就可以解释了:
         CLTest是AppClassloader加载的,String是通过加载CLTest的类加载器也就是AppClassloader进行加载,但最终委派到bootstrap加载的(当然,String类其实早已经被加载过了,这里只是举个例子)。所以,对于String类来说,bootstrap是“定义类加载器”,AppClassloader是“初始类加载器”。根据刚才所说,String类在AppClassloader的命名空间中(同时也在bootstrap,ExtClassloader的命名空间中,因为bootstrap,ExtClassloader也是String的初始类加载器),所以CLTest可以随便访问String类。这样就可以解释“处在不同命名空间的类,不能直接互相访问”这句话了。

    3、一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例。

    4、那么由不同类加载器实例(比如-自定义clsloadr1,-自定义clsloadr2)所加载的classpath下和ext下的类,也就是由我们自定义的类加载器委派给AppClassloader和ExtClassloader加载的类,在内存中是同一个类吗?
        所有继承ClassLoader并且没有重写getSystemClassLoader方法的类加载器,通过getSystemClassLoader方法得到的AppClassloader都是同一个AppClassloader实例,类似单例模式。
    在ClassLoader类中getSystemClassLoader方法调用私有的initSystemClassLoader方法获得AppClassloader实例,在initSystemClassLoader中:
      sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
      。。。
      scl = l.getClassLoader();
        AppClassloader是sun.misc.Launcher类的内部类,Launcher类在new自己的时候生成AppClassloader实例并且放在自己的私有变量loader里:
      loader = AppClassLoader.getAppClassLoader(extclassloader);

        值得一提的是sun.misc.Launcher类使用了一种类似单例模式的方法,即既提供了单例模式的接口getLauncher()又把构造函数设成了public的。但是在ClassLoader中是通过单件模式取得的Launcher 实例的,所以我们写的每个类加载器得到的AppClassloader都是同一个AppClassloader类实例。
    这样的话得到一个结论,就是所有通过正常双亲委派模式的类加载器加载的classpath下的和ext下的所有类在方法区都是同一个类,堆中的Class实例也是同一个。

    三、本文重点:

        有一个上下文加载器,contextClassLoader,它存在于当前线程中,默认为SystemClassLoader,那么它有什么用呢?大部分情况下,我们加载类,jvm默认的委托加载体系是可以满足需求的,但是有一些特殊情况要另当别论,这就是rt.jar中的spi服务,最常见的就是jdbc了,它存在的包rt.jar是由BootStrapClassLoader加载的,但是在它的DriverManager中会调用spi具体的实现类,如mysql,但是具体的实现类是由SystemClassLoader加载的,根据隔离原则,DriverManager中是访问不到mysql具体方法的,那怎么办呢?具体先看DriverManager代码:

         看到的是,会在DriverManager里使用contextClassLoader去加载具体mysql的实现类,所以BootStrapClassLoader就是mysql实现类的初始加载器,contextClassLoader(SystemClassLoader)是mysql的定义类加载器,这样的话,在BootStrapClassLoader命名空间中就会有mysql类,所以DriverManager里面就可以直接访问mysql类了,这其实打破了类的委托机制,这种机制普遍存在于好多第三方的工具中,如tomcat、spring中。

        通过上面的两个案例分析,我们可以总结出线程上下文类加载器的适用场景:(https://www.2cto.com/kf/201609/551006.html)

        1、 当高层提供了统一接口让低层去实现,同时又要是在高层加载(或实例化)低层的类时,必须通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。

        2、当使用本类托管类加载,然而加载本类的ClassLoader未知时,为了隔离不同的调用者,可以取调用者各自的线程上下文类加载器代为托管。

  • 相关阅读:
    Python基础知识篇
    Django框架
    Django REST Framework框架
    NoSQL
    MySQL恩恩怨怨
    Python奇技淫巧
    一文搞定Flask
    数据结构与算法(Python)
    学习数据分析
    项目杂项
  • 原文地址:https://www.cnblogs.com/sjjsh/p/11774087.html
Copyright © 2020-2023  润新知