插件系统怎么用 URLClassLoader 动态加载 JAR、替换版本、卸载?这篇用两个版本的插件 jar 逐项验证加载、类身份、卸载与泄漏(类加载器的双亲委派基础见前面的篇目,Module Layer 在后面)。
从 jar 里加载 Impl 并转成 Plugin 接口成功:impl-v1 → v1:HELLO;Impl 的类加载器 = URLClassLoader,Plugin 接口的类加载器 = AppClassLoader
Impl.class.getProtectionDomain().getCodeSource() = …/plugin_v1.jar
用 Class.forName("Impl")(用调用者的类加载器)→ ClassNotFoundException(插件类只在 URLClassLoader 里)jar 里自带了一份 Plugin 接口,父加载器设为 null(启动类加载器):Impl 的接口 Plugin 来自 URLClassLoader;与应用里的 Plugin.class 是同一个类吗:false (Plugin) 转型 → ClassCastException: class Impl cannot be cast to class Plugin (Impl is in unnamed module of loader java.net.URLClassLoader @…; Plugin is in unnamed module of loader 'app') 同一个 jar(也带着 Plugin 接口),但父加载器是应用类加载器:先问父加载器,接口用的是应用的那一份,转型成功:v1x:x;接口的加载器 = AppClassLoader
plugin_v1.jar → impl-v1:v1:HELLO plugin_v2.jar → impl-v2:v2:olleh 两个 URLClassLoader 各自加载同一个 jar 的 Impl:c1 == c2 吗?false,c1.getName().equals(c2.getName()) = true
加载后进程打开了 plugin_v1.jar 的文件描述符个数(lsof):1 close() 之后:0 个(关闭了 jar 文件) close() 之后还有强引用(pl 与 cl 变量)时,GC 能回收这个类加载器吗:false 把 pl 与 cl 都置为 null 之后,GC 后类加载器被回收:true,插件的 Class 被回收:true
插件里启动了一个后台线程(类里有 static 块起线程),并持有 8 MB 的静态数组:卸载插件(close、丢弃引用、GC)之后,类加载器被回收了吗:false plugin-thread 线程还在运行吗:true(线程还活着时类加载器没有被回收;具体是哪条引用链留住它,本篇没有查)