关于Java类加载器的一些记录
发布时间:2020-10-14 09:15:07 阅读:375

为什么会出现加载Excel插件出现POI 库中的类加载不成功问题(NoClassDefFoundError)?

NoClassDefFoundError: 连接时异常 此时类是否已经 初始化 待探究【虽然SecureClassLoader源码中看到了initialized=true】
可能是在类加载过程, 解析环节:将常量池中的符号引用转为直接引用出现问题

《Java虚拟机规范》之中并未规定解析阶段发生的具体时间,只要求了在执行ane-warray、 checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invoke-special、 invokestatic、invokevirtual、ldc、ldc_w、ldc2_w、multianewarray、new、putfield和putstatic这17个用于 操作符号引用的字节码指令之前,先对它们所使用的符号引用进行解析。所以虚拟机实现可以根据需 要来自行判断,到底是在类被加载器加载时就对常量池中的符号引用进行解析,还是等到一个符号引 用将要被使用前才去解析它。

  • 2022/02/25 测试 loadClass的时候是否会进行 初始化 【会,28号的测试&源码 都能测试出来】
  • 2022/02/25 为什么loadClass() cglib中的类会报错呢? 【因为 验证没有通过】

报错java.lang.NoClassDefFoundError: org/apache/tools/ant/Task
https://mvnrepository.com/artifact/cglib/cglib/3.3.0
上述连接中是描述 cglib 依赖于 org.apache.ant » ant 的

  • 2022/02/28 这个org.apache.ant是可选的。目前没有人用它。但是你每个都loadClass一遍 就会验证不通过

此时该类依赖的POI相关类对此加载器是不可见的,所以抛出NoClassDefFoundError

解决该ERROR有几种解决办法:

  • UrlClassLoader 加载 该POI的类(现方案)

优点:最适合插件场景
缺点:不同类加载器会重复加载一个类 可能内存占用会多

  • 让UrlClassLoader的父加载器 加载该POI的类

优点:符合双亲委派规则,不浪费内存
缺点:不适合插件环境(无法预知用户会使用哪些三方库)

为什么自定义ClassLoader的时候即便加载了代码有问题的类,也不会在加载的时候报错?

  1. 可能是在类加载过程,没有进入解析环节(符号引用转为直接引用这步骤还没有触发),也不会初始化(clinit)

加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按 照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始

如果解决以上问题,上述重复loadClass操作会不会出现问题

loadClass就是调用双亲委派方式。重复该行为是否有问题,还没有调研到

模块化的三方库需要怎么使用自定义的ClassLoader加载?

![[00 assets/Pasted image 20220222153720.png]]
JDK 9中虽然仍然维持着三层类加载器和双亲委派的架构,但类加载的委派关系也发生了 变动。当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能 够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器 完成加载
所以当前使用UrlClassLoader,并且指定parent为AppClassLoader的代码是可靠的。

【但是当前加载方式 是走的下面哪个规则,还没有研究出来。】

  • JAR文件在类路径的访问规则:所有类路径下的JAR文件及其他资源文件,都被视为自动打包在 一个匿名模块(Unnamed Module)里,这个匿名模块几乎是没有任何隔离的,它可以看到和使用类路 径上所有的包、JDK系统模块中所有的导出包,以及模块路径上所有模块中导出的包。
  • 模块在模块路径的访问规则:模块路径下的具名模块(Named Module)只能访问到它依赖定义 中列明依赖的模块和包,匿名模块里所有的内容对具名模块来说都是不可见的,即具名模块看不见传 统JAR包的内容。
  • JAR文件在模块路径的访问规则:如果把一个传统的、不包含模块定义的JAR文件放置到模块路 径中,它就会变成一个自动模块(Automatic Module)。尽管不包含module-info.class,但自动模块将 默认依赖于整个模块路径中的所有模块,因此可以访问到所有模块导出的包,自动模块也默认导出自 己所有的包。

目前的插件运行管理系统遵循了哪种模式?

双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷导致的,双亲委派很好地解决了各个类 加载器协作时基础类型的一致性问题(越基础的类由越上层的加载器进行加载),基础类型之所以被 称为“基础”,是因为它们总是作为被用户代码继承、调用的API存在,但程序设计往往没有绝对不变 的完美规则,如果有基础类型又要调用回用户的代码,那该怎么办呢?

这并非是不可能出现的事情,一个典型的例子便是JNDI服务,JNDI现在已经是Java的标准服务, 它的代码由启动类加载器来完成加载(在JDK 1.3时加入到rt.jar的),肯定属于Java中很基础的类型 了。但JNDI存在的目的就是对资源进行查找和集中管理,它需要调用由其他厂商实现并部署在应用程 序的ClassPath下的JNDI服务提供者接口(Service Provider Interface,SPI)的代码,现在问题来了,启 动类加载器是绝不可能认识、加载这些代码的,那该怎么办?

为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器 (Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方 法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内 都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

  1. 与JNDI的设计思想一样,打破双亲委派的模式(父类加载器调用ContextClassLoader[子加载器?])
发表评论
使用 Nuxt 3 构建 | 部署于 Kubernetes | 托管于 狗云
Copyright © 2020-2024 | 网站已续航 1743 天