Java类加载器ClassLoader

         Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。

 

类加载器:

        对于HotSpot虚拟机而言,类加载器大体分为两种:属于JVM本身的类加载器,如:Bootstrap ClassLoader;不属于JVM本身的类加载器,如:Extensions ClassLoader、Application ClassLoader、用户自定义类加载器:

◎Bootstrap ClassLoader(引导类加载器):

        HotSpot虚拟机(由C、C++等语言编写)本身包含了一个名为Bootstrap ClassLoader的类加载器;BootstrapClassLoader是用C++编写的,它负责将核心JavaClass加载到虚拟机内存中。
注:核心JavaClass指:
       1、<JAVA_HOME>/jre/lib下的且被虚拟机(按名称)识别的类库,如:rt.jar
       2、被-Xbootclasspath参数指定的路径中的 且被拟机识别的类库

注:在编写自定义的加载器时,并不能被显示的指定parent为Bootstrap ClassLoader,如果想把parent设置为引导类
        加载器,那么直接使parent值为null即可;或者直接就想用引导类加载器的话,直接使该加载器为null即可。

◎ExtClassLoader(即:扩展类加载器):

       此加载器本身由BootstrapClassLoader加载;负责加载扩展的Javaclass。该类处于sun.misc.Launcher$ExtClassLoader。
注:扩展JavaClass指:
      1、<JAVA_HOME>/jre/ext下的所有类库
      2、系统变量java.ext.dirs指定的目录下的所有类库

◎AppClassLoader(也称:App类加载器、系统类加载器):

       此加载器本身由BootstrapClassLoader加载;负责加载Java应用程序类路径(classpath路径)下的类库。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。该类处于sun.misc.Launcher$AppClassLoader。

最基本的加载流程
        当运行一个程序的时候,JVM启动,运行BootstrapClassLoader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。

 

类加载的双亲委派模式:

测试ClassLoader的parent属性

注:类加载器之间的类关系是关联而不是泛化(详细点说:是组合而不是继承)。AppClassLoader的parent是 
       ExtClassLoader,ExtClassLoader的parent是null(注:当一个ClassLoader的parent为null时,就说明这个
       类加载器的parent是Bootstrap ClassLoader)。

双亲委派模式里,一个类的加载流程:

假设现在用户自定义了一个类加载器:

 

注:自定义类加载器,需要继承ClassLoader,并重写findClass方法
       追注:JDK1.2之后就不推荐重写loadClass方法了,而是推荐重写findClass方法,在
                   protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
                  方法里会调用protected Class<?> findClass(String name) throws ClassNotFoundException方法,
                  详见源码。

那么加载一个类的流程为:

注:双亲委派模式很好的解决了各个加载器对基础类的加载的问题(越基础的类,越上层加载),保证了基础类库的安
       全性。

类加载器的再演变(双亲委派模型的破坏):

        双亲委派模式只是类加载器设计者推荐的一种模式,并不是一定要遵循的。在某些场景里,可能或用到直接加载某些类。

        如JNDI(即:Java Naming and Directory Interface),JNDI是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,由不同的独立厂商来对这些接口进行各自的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。

       JNDI位于rt.jar下,由Bootstrap ClassLoader加载,JNDI的目的是对资源进行集中管理和查找,JNDI需要调用由第三方独立厂商提供并部署在应用程序的classpath下SPI(Service Provider Interface)实现类。因为Bootstrap ClassLoader并不能加载这些类,为了解决这个问题,Java设计团队就引入了Thread Context ClassLoader线程上下文类加载器。这样一来Bootstrap ClassLoader加载JNDI后,JNDI再通过Thread Context ClassLoader去加载对应的类到虚拟机内存(注:这样其实就打破了双亲委托原则了)。
注:Thread Context ClassLoader可以通过 java.lang.Thread类的setContextClassLoaser()方法进行设置;如果
       创建线程时还未设置,它将会从父线程中继承个,如果在应用程序的全局范围内都没有设置过的话,那这个类加
       载器默认就是AppClassLoader。

        随着用户对程序动态性(如:代码热替换、模块儿热部署)的追求,类加载又有进一步的演变。OSGI(Open Service Gateway Initiative)技术是Java动态化模块化系统的一系列规范。在OSGI环境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构,当收到类加载请求时,OSG将按照下面的顺序进行类搜索:

  1. 将以java.*开头的类委派给父类加载器加载。
  2. 否则,将委派列表名单内的类委派给父类加载器加载。
  3. 否则,将 Import列表中的类委派给 Export这个类的 Bundle的类加载器加载。
  4. 否则,查找当前 Bundle的 Class path,使用自己的类加载器加载。
  5. 否则,查找类是否在自己的 Fragment Bundle中,如果在,则委派给 Fragment Bundle的类加载器加载。
  6. 否则,查找 Dynamic Import列表的 Bundle,委派给对应 Bundle的类加载器加载。
  7. 否则,类查找失败。

注上述步骤中1、2步走的还是双亲委派模式。

 

声明:本人部分内容直接摘录自《深入理解Java虚拟机》 周志鹏著。

 

微笑如有不当之处,欢迎指正

微笑参考资料:
           
Java虚拟机JVM高级特性与最佳实践》 周志鹏著

微笑参考链接:
           https://www.cnblogs.com/doit8791/p/5820037.html
           https://baike.baidu.com/item/classloader/1148210

微笑本文已经被收录进《程序员成长笔记(四)》,笔者JustryDeng

猜你喜欢

转载自blog.csdn.net/justry_deng/article/details/85260350