S++

千线一眼

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

JVM-类加载(5)

类加载器

jdk8 为例:

名称 加载什么地方的类 说明
Bootstrap ClassLoader JAVA_HOME/jre/lib 无法直接访问
Extension ClassLoader JAVA_HOME/jre/lib/ext 上级为Bootstrap,显示为null
Application ClassLoader classpath 上级为Extension
自定义类加载 自定义 上级为Application

启动类加载器

  • -Xbootclasspath 表示设置 bootclasspath
    • java -Xbootclasspath: 完全替换jre下的lib
    • java -Xbootclasspath/a:<追加路径> 追加一个路径(后追加)
    • java -Xbootclasspath/p:<追加路径> 追加一个路径(前追加)

扩展类加载器

顺序:启动类 > 扩展类 > 应用类
调用类加载器的loadClass方法时,查找类的规则 —— 双亲委派模式
这里的双亲,理解为上级更加合适,他们之间并没有继承关系
规则:

  • 1-检查该类是否已经加载
  • 2-有上级则委派上级 loadClass
  • 3-没有上级,委派启动类加载器Bootstrap ClassLoader
  • 4-每一层都找不到,调用findClass方法(每个类加载器自己扩展)加载

线程上下文类加载器

我们使用过JDBC,其中需要

Class.forName("com.mysql.jdbc.Driver")

但是如果不写上面的代码,我们依旧能够让驱动正常加载
DriverManager部分源码:

public class DriverManager {
  // 注册驱动集合
  private final static CopyOnWriterArrayList<DriverInfo> registeredDrivers = new CopyOnWriterArrayList();

  // 初始化驱动
  static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
  }
}

我们通过

System.out.println(DriverManager.class.getClassLoader());

得到结果为null,可知其类加载器是 Bootstrap ClassLoader,会在 JAVA_HOME/jre/lib 下搜索类
但在此路径下并没有显示有相关的驱动jar包,那么它是如何正确加载到的呢?


原因在于 loadInitialDrivers() 方法:
JDK在某些情况下需要打破双亲委派模式,有时候会调用应用程序类加载器来进行加载,否则有些类是找不到的
在这个方法中使用了 Service Provider Interface(SPI)接口进行解耦合
使用要求在jar包中新增一个 META-INF 目录,目录下services目录存放接口全限定名文件用于存放接口的实现类名
目录结构

ServiceLoader.load方法:

public static <S> ServiceLoader<S> load(Class<S> service) {
  // 获取线程上下文类加载器
  ClassLoader c1 = Thread.currentThread().getContextClassLoader();
  return ServiceLoader.load(service c1);
}

线程上下文类加载器是当前线程使用的类加载器,默认就是应用程序类加载器,
它内部又是由 Class.forName调用了线程上下文类加载器完成类加载,
具体代码在ServiceLoader 的内部类 LazyIterator中……


自定义类加载器

什么时候需要自定义类加载器:

  • 想加载非classpath 随意路径中的类文件
  • 都是通过接口来使用实现,希望解耦合时,常用在框架设计
  • 这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于 tomcat 容器

使用步骤:

  • 1 继承 ClassLoader 父类
  • 2 要遵从双亲委派机制,重写 findClass 方法
    • 注意不是重写 loadclass 方法,否则不会走双亲委派机制
  • 3 读取类文件的字节码
  • 4 调用父类的defineClass 方法来加载类
  • 5 使用者调用该类加载器的 1oadClass 方法

posted on   S++  阅读(29)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示