ClassLoader

115 阅读2分钟

分类

image.png

  • 启动类加载器(Bootstrap Class Loader)
    • 加载 Java 核心库,位于 <JAVA_HOME>\lib 目录,或被 -Xbootclasspath 指定的路径中存放的能够被 JVM 识别的类(名称为 rt.jar、tools.jar 等,名字不符合的类库机制放在 lib 目录中也不会被加载)
    • 启动类加载器无法被 Java 程序直接引用,有以 null 值来代表引导类加载器的约定规定
  • 扩展类加载器(Extension Class Loader)
    • 体现在 sun.misc.Launcher.$ExtClassLoader 上,是一种 Java 系统类库的扩展机制
    • 加载 <JAVA_HOME>\lib\ext 目录,或者被 java.ext.dirs 系统变量指定的路径中的类库
  • 应用程序类加载器(Application Class Loader)
    • 体现在 sun.misc.Launcher.$AppClassLoader 上,是 ClassLoader.getSystemClassLoader() 方法的返回值,所以,又称 “系统类加载器”
    • 加载用户类路径 -classpath 参数指定的类库

双亲委派模型

“双亲委派模型” 用来形容 ClassLoader 之间的关系:除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。

优点

保护程序的安全,防止核心 API 被随意篡改(这种保护,也被看作“沙箱安全机制”)。

如:java.lang.Object 在 rt.jar 中,无论哪个类加载器加载这个类,最终都委派给 Bootstrap ClassLoader 进行加载,因此 Object 类在程序的各种类加载器环境中能够保证是一个类。

破坏

双亲委派模型在 JDK 1.2 时被引入,是 Java 推荐的一种类加载器的最佳实践,并不具有强制性约束力,但在一些场景中,也会根据需求来打破这个模式,来实现必要的功能,如:

  • Java SPI
  • OSGi 模块热部署、代码热替换
  • Tomcat

工作过程

  1. 自下而上请求)当一个类加载器收到了类加载的请求,首先不会自己尝试加载,而是把这个请求委派给父类加载器去完成,
  2. 从上往下加载)只有当父加载器的搜索范围中没有找到这个类时,子类加载器才会尝试自己去完成加载。

实现原理

  1. 使用组合关系来复用父加载器的代码

image.png

  1. java.lang.ClassLoader#loadClass(java.lang.String, boolean) 中,实现双亲委派模型

image.png

parent.loadClass(name, false); 中请求父加载器进行加载,当加载失败时(c == null),由当前类加载器使用 c = findClass(name); 进行加载。

Tips:

  1. 建议重写 loadClass 实现自定义类加载器,优雅的做法是将自定义类加载逻辑写在 findClass 中;
  2. 如果没有特别复杂的需求,可继承 URLClassLoader,少些一些逻辑。

自定义类加载器

自定义类加载器的动机是什么?

  • 实现类的隔离,如:Tomcat
  • 破坏双亲委派模型
  • 扩展加载源,如:从 MySQL 中加载 jar
  • 代码加密,防止源码泄露

参考

  1. 《深入理解Java虚拟机》
  2. Java 8 源码