升级JDK8至JDK11后,sun.misc.URLClassPath替代类咨询
JDK 11中替代sun.misc.URLClassPath实现运行时Jar卸载的方案
问题描述
我有一个基于JDK 8实现的运行时卸载.jar文件的方法,升级到JDK 11后,程序无法识别sun.misc.URLClassPath类。请问JDK 11中有哪个类可以替代它?
原JDK 8实现代码:
import sun.misc.URLClassPath; public static boolean unloadJar(String jarFile, ClassLoader classLoader) { try { if (classLoader instanceof URLClassLoader) { URLClassPath ucp = (URLClassPath)getField(classLoader, "ucp", URLClassLoader.class); ArrayList<URL> path = (ArrayList<URL>)getField(ucp, "path"); ArrayList<?> loaders = (ArrayList<?>)getField(ucp, "loaders"); boolean isRemovedFromPath = path.removeIf(p -> p.getFile().endsWith(jarFile)); boolean isRemovedFromLoaders = loaders.removeIf(l -> { try { URL csu = (URL)getField(l, "csu"); return csu.getFile().endsWith(jarFile); } catch (NoSuchFieldException | IllegalAccessException e) { return false; } }); return isRemovedFromPath && isRemovedFromLoaders; } else { String msg = String.format("Class loader type is mismatched. Required: %s, actual: %s", classLoader.getClass().getName(), URLClassLoader.class.getName()); logger.debug(msg); return false; } } catch (Exception e) { return false; } }
解决方案
sun.misc.URLClassPath是JDK内部非公开API,从JDK 9开始被模块化系统隐藏,JDK 11中已完全移除,没有直接对应的替代类。Java官方从设计上不推荐依赖内部API实现Jar卸载,更规范的做法是通过自定义类加载器实现动态Jar加载与卸载:
核心原理
Java中类的生命周期与加载它的类加载器绑定,当自定义类加载器被GC回收(且无任何该类加载器加载的类实例、静态引用存在),其加载的所有类也会被自动卸载。
实现示例
- 自定义URL类加载器
import java.net.URL; import java.net.URLClassLoader; public class JarClassLoader extends URLClassLoader { public JarClassLoader(URL[] urls, ClassLoader parent) { // 传入父类加载器遵循双亲委派模型 super(urls, parent); } // 动态添加Jar的方法 public void addJar(URL jarUrl) { super.addURL(jarUrl); } }
- 加载与卸载Jar的使用方式
import java.net.MalformedURLException; import java.net.URL; public class JarUnloadDemo { public static void main(String[] args) throws Exception { // 1. 加载目标Jar URL jarUrl = new URL("file:/path/to/your/target.jar"); JarClassLoader jarLoader = new JarClassLoader(new URL[]{jarUrl}, Thread.currentThread().getContextClassLoader()); // 2. 使用类加载器加载并调用类(示例) Class<?> demoClass = jarLoader.loadClass("com.example.DemoClass"); Object instance = demoClass.getDeclaredConstructor().newInstance(); demoClass.getMethod("demoMethod").invoke(instance); // 3. 执行卸载操作 // 释放所有对类实例的引用 instance = null; // 释放对类加载器的引用 jarLoader = null; // 提示:实际业务中无需手动调用GC,JVM会自动在合适时机回收 System.gc(); } }
注意事项
- 避免使用系统类加载器或应用类加载器加载需要卸载的Jar,因为这些类加载器通常会被JVM长期持有,无法被GC回收。
- 确保所有通过自定义类加载器加载的类实例、静态变量等引用都被清空,否则类加载器无法被回收,Jar也无法卸载。
- 原代码中通过反射修改内部类字段的方式在JDK 11及以后完全不可行,模块化系统会阻止对内部API的反射访问,即使开启相关权限也不推荐(后续版本可能彻底禁用)。
内容的提问来源于stack exchange,提问作者NoName
相关产品推荐
相关产品推荐

