ClassLoader-浅析

曲径通幽处

什么是ClassLoader?

这个简单,直接看ClassLoader类源码:

/**
 * A class loader is an object that is responsible for loading classes. The
 * class <tt>ClassLoader</tt> is an abstract class.  Given the <a
 * href="#name">binary name</a> of a class, a class loader should attempt to
 * locate or generate data that constitutes a definition for the class.  A
 * typical strategy is to transform the name into a file name and then read a
 * "class file" of that name from a file system.
 *  * <p> Every {@link Class <tt>Class</tt>} object contains a {@link
 * Class#getClassLoader() reference} to the <tt>ClassLoader</tt> that defined
 * it.
 *  * <p> <tt>Class</tt> objects for array classes are not created by class
 * loaders, but are created automatically as required by the Java runtime.
 * The class loader for an array class, as returned by {@link
 * Class#getClassLoader()} is the same as the class loader for its element
 * type; if the element type is a primitive type, then the array class has no
 * class loader.
 *  * <p> Applications implement subclasses of <tt>ClassLoader</tt> in order to
 * extend the manner in which the Java virtual machine dynamically loads
 * classes.
 *  * <p> Class loaders may typically be used by security managers to indicate
 * security domains.
 *  * <p> The <tt>ClassLoader</tt> class uses a delegation model to search for
 * classes and resources.  Each instance of <tt>ClassLoader</tt> has an
 * associated parent class loader.  When requested to find a class or
 * resource, a <tt>ClassLoader</tt> instance will delegate the search for the
 * class or resource to its parent class loader before attempting to find the
 * class or resource itself.  The virtual machine's built-in class loader,
 * called the "bootstrap class loader", does not itself have a parent but may
 * serve as the parent of a <tt>ClassLoader</tt> instance.
 *  * <p> Class loaders that support concurrent loading of classes are known as
 * <em>parallel capable</em> class loaders and are required to register
 * themselves at their class initialization time by invoking the
 * {@link
 * #registerAsParallelCapable <tt>ClassLoader.registerAsParallelCapable</tt>}
 * method. Note that the <tt>ClassLoader</tt> class is registered as parallel
 * capable by default. However, its subclasses still need to register themselves
 * if they are parallel capable. <br>
 * In environments in which the delegation model is not strictly
 * hierarchical, class loaders need to be parallel capable, otherwise class
 * loading can lead to deadlocks because the loader lock is held for the
 * duration of the class loading process (see {@link #loadClass
 * <tt>loadClass</tt>} methods).
 *  * <p> Normally, the Java virtual machine loads classes from the local file
 * system in a platform-dependent manner.  For example, on UNIX systems, the
 * virtual machine loads classes from the directory defined by the
 * <tt>CLASSPATH</tt> environment variable.
 *  * <p> However, some classes may not originate from a file; they may originate
 * from other sources, such as the network, or they could be constructed by an
 * application.  The method {@link #defineClass(String, byte[], int, int)
 * <tt>defineClass</tt>} converts an array of bytes into an instance of class
 * <tt>Class</tt>. Instances of this newly defined class can be created using
 * {@link Class#newInstance <tt>Class.newInstance</tt>}.
 *  * <p> The methods and constructors of objects created by a class loader may
 * reference other classes.  To determine the class(es) referred to, the Java
 * virtual machine invokes the {@link #loadClass <tt>loadClass</tt>} method of
 * the class loader that originally created the class
 */
public abstract class ClassLoader {
  private final ClassLoader parent;
  //省略若干代码
}

很明显,这是一个抽象类,内部还有一个parent指向ClassLoader类型的对象,其他就是有很多注释,这里整理下注释中的内容:

  • ClassLoader的职责是加载一个类的原始数据到内存,生成运行时的类描述对象,可以理解将虚拟机外部字节码加载到虚拟机内部,最终在内存中生成类Class对象。而ClassLoader类的实现者主要就是扩展JVM加载Class的能力,因为ClassLoader并没有限制加载来源,所以可以从本地、网络、或者其他任意你想的地方加载Class
  • 每个生成的Class对象都包含一个引用指向加载该Class的ClassLoader
  • 是不是每一个Java中的Class都是ClassLoader生成的呢?其实不是。注释了解释了,数组对应的Class不是由ClassLoader生成的,而是由JVM直接生成的,但是数组中的元素(原始类型除外)相关的Class是由ClassLoader生成
  • ClassLoader委托模型(就是“双亲委托机制”)来实现,观察到ClassLoader类中parent成员了吗,其就是用来实现向上委托的,熟悉设计模式的小伙伴应该也感受到了,怎么有点像责任链模式,哈哈哈哈😂。

那怎么实用呢?来个官方示例:

//从网络中加载一个Class
class NetworkClassLoader extends ClassLoader {
       String host;
       int port;
       //重写findClass,根据class name 返回一个Class
      public Class findClass(String name) {
        byte[] b = loadClassData(name);
         return defineClass(name, b, 0, b.length);//调用native 方法 defineClass将byte[]转化为Class
     }

      private byte[] loadClassData(String name) {
        // 从网络中加载类的字节数组
      }
  }

ClassLoader工作原理

public abstract class ClassLoader {
  private final ClassLoader parent;
  //...
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        //synchronized关键字,解决多线程情况下同步问题,因为一个类只需要被加载一次
        synchronized (getClassLoadingLock(name)) {
            // 首先,检查该类是否已经被加载了,返回null表示暂时没有被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                   //如果parent不为null,先委托给pareny去加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                       //parent为null时,则委托findBootstrapClassOrNull方法返回的类加载器去加载,
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 如果仍然没有加载成功,则调用findClass方法去加载类,就是之前我们实例中重写的findClass方法
                    long t1 = System.nanoTime();
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
  }
  }

上述源码中,我们还不知道findBootstrapClass方法返回的是什么加载器,这里介绍下Jvm中的加载器:

JVM 运行实例中会存在多个 ClassLoader,不同的 ClassLoader 会从不同的地方加载字节码文件。它可以从不同的文件目录加载,也可以从不同的 jar 文件中加载,也可以从网络上不同的服务地址来加载。JVM 中内置了三个重要的 ClassLoader,分别是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader。
BootstrapClassLoader 负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,我们常用内置库 java.xxx.* 都在里面,比如 java.util.、java.io.、java.nio.、java.lang. 等等。这个 ClassLoader 比较特殊,它是由 C 代码实现的,我们将它称之为「根加载器」。
ExtensionClassLoader 负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。
AppClassLoader 才是直接面向我们用户的加载器,它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。
那些位于网络上静态文件服务器提供的 jar 包和 class文件,jdk 内置了一个 URLClassLoader,用户只需要传递规范的网络路径给构造器,就可以使用 URLClassLoader 来加载远程类库了。URLClassLoader 不但可以加载远程类库,还可以加载本地路径的类库,取决于构造器中不同的地址形式。ExtensionClassLoader 和 AppClassLoader 都是 URLClassLoader 的子类,它们都是从本地文件系统里加载类库。
AppClassLoader 可以由 ClassLoader 类提供的静态方法 getSystemClassLoader() 得到,它就是我们所说的「系统类加载器」,我们用户平时编写的类代码通常都是由它加载的。当我们的 main 方法执行的时候,这第一个用户类的加载器就是 AppClassLoader。

这里findBootstrapClass方法返回的就是BootstrapClassLoader。那么整理下,其findClass的大致加载流程如下
双亲委托

ClassLoader有那些性质?

延迟加载

ClassLoader不是一次加载所有的类到内存,而是按需加载,只有需要触发初始化的类或者直接引用到的类才会进行加载。
触发类初始化的有:

  • 使用new和反射创建对象
  • 直接访问静态方法、静态变量
  • 子类初始化时,必定先初始化父类,接口除外
传递性

传递性主要体现在两个方向上:

  1. 由于一个类A引用到还未加载的类B时,类B的加载由类A的加载完成
  2. 如果没有通过Thread的setContextClassLoader方法设置ClassLoader,那么子线程会和创建ta的线程使用同一个ClassLoader
安全性

双亲委托机制使得具有层次和域,公共类始终由根加载器加载,越基础的类,其访问域越大,而会被更安全的加载器加载,从而达到保护Jvm的目的,大家可以想一下,自己写一个java.lang.Object类,试试看会不会正常运行?会出现什么情况。

ClassLoader注意问题

类版本冲突问题

https://zhuanlan.zhihu.com/p/141527120
https://developer.ibm.com/zh/articles/j-lo-classloader/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值