zoukankan      html  css  js  c++  java
  • JVM——三个ClassLoader详解

      类装载工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java执行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生三个ClassLoader:根装载器ExtClassLoader(扩展类装载器)和AppClassLoader(系统类装载器)。其中,根装载器不是ClassLoader的子类,它使用C++编写,因此我们在Java中看不到它,根装载器负责装载JRE的核心类库,如JRE目标下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子类。其中ExtClassLoader负责装载JRE目录ext中的JAR类包;AppClassLoader负责装载ClassPath路径下的类包。

    • 启动类加载器(Bootstrap ClassLoader):这个类加载器负责将存放在<JAVA_HOME>lib目录中的。启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可。
    • 扩展类加载器(Extension ClassLoader):这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>libext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
    • 应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器。

      我们的应用程序都是由这3种类加载器互相配合进行加载的,如果有必要,还可以加入自己定义的类加载器。这些类加载器之间的关系一般为:

      上图展示的类加载器之间的这种层次关系,称为类加载器的双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承的关系来实现,而是都使用组合关系来复用父加载器的代码。

      类加载器的双亲委派模型详解博客链接

      这三个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是父类装载器。默认情况下,使用AppClassLoader装载应用程序的类,用以下代码证明:

     1 /**
     2  * @author zhengbinMac
     3  */
     4 public class ClassLoaderTest {
     5 
     6     public static void main(String[] args) {
     7         ClassLoader loader = Thread.currentThread().getContextClassLoader();
     8         System.out.println("current loader:" + loader);
     9         System.out.println("parent loader:" + loader.getParent());
    10         System.out.println("grandparent loader:" + loader.getParent().getParent());
    11     }
    12     /*
    13      * output:
    14      *    current loader:sun.misc.Launcher$AppClassLoader@1b6d3586
    15      *    parent loader:sun.misc.Launcher$ExtClassLoader@1540e19d 
    16      *    grandparent loader:null // 因为根类装载器在Java中访问不到,所有返回null
    17      */
    18 }

      Thread.currentThread():返回对当前正在执行的线程对象的引用。

      getContextClassLoader():返回该线程的上下文 ClassLoader。

      通过以上的输出信息,可以明白,ClassLoader是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader是根类装载器,因为在Java中无法获得它的句柄,所以直接返回null。

      除了JVM默认的三个ClassLoader以外,可以编写自己的第三方类装载器,以实现一些特殊的需求。类文件被装载并解析后,在JVM内将拥有一个对应的java.lang.Class类描述对象,该类的实例都拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联ClassLoader的引用。如下图所示:

    ClassLoader重要方法


      在Java中,ClassLoader是一个抽象类,位于java.lang包中。下面对该类的一些重要接口方法进行介绍:

    • Class loadClass(String name)

      name参数指定类装载器需要装载类的名字,必须使用全限定类名,如com.zhengbin.entity.Student。该方法有一个重载方法loadClass(String name, boolean resolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要进行解析。

    • Class defineClass(String name, byte[] b, int off, int len)

      将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。

    • Class findSystemClass(String name)

      从本地文件系统载入Class文件,如果本地文件系统不存在该Class文件,将抛出ClassNotFoundException异常。该方法是JVM默认使用的装载机制。

    • ClassLoader getParent()

      获取类装载器的父装载器,除根装载器外,所有的类装载器都有且仅有一个父装载器,ExtClassLoader的父装载器是根装载器,因为根装载器非Java编写,所以无法获得,将返回null。

  • 相关阅读:
    HDU 1501 Zipper(DFS)
    HDU 2181 哈密顿绕行世界问题(DFS)
    HDU 1254 推箱子(BFS)
    HDU 1045 Fire Net (DFS)
    HDU 2212 DFS
    HDU 1241Oil Deposits (DFS)
    HDU 1312 Red and Black (DFS)
    HDU 1010 Tempter of the Bone(DFS+奇偶剪枝)
    HDU 1022 Train Problem I(栈)
    HDU 1008 u Calculate e
  • 原文地址:https://www.cnblogs.com/zhengbin/p/5631987.html
Copyright © 2011-2022 走看看