我正在尝试了解 Java 中的类卸载是如何工作的。我创建了一个测试应用程序,它只加载一个类,然后等待。
package com.expirement;
import java.net.URL;
import java.net.URLClassLoader;
public class Main {
public static void main(String[]args) throws Exception {
f();
try{
Thread.sleep(5000);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
//breakpoint
}
public static void f() throws Exception {
URLClassLoader cl=new URLClassLoader(new URL[]{
Main.class.getProtectionDomain().getCodeSource().getLocation()
});
Class<?> c = cl.loadClass ("com.expirement.Loadable1");
cl.close();
System.gc();
cl = null;
c = null;
}
}
不幸的是,在该断点处,JVM 尚未卸载该类。
我的问题是:
当您未指定父加载器时,
URLClassLoader
将使用应用程序类加载器,并且由于您使用的是类路径中的 URL,因此可以通过此父加载器解析请求的类。
您可以指定引导加载程序(表示为
null
)作为父加载程序。
进一步注意,当您将变量设置为
null
或在触发垃圾收集之前保留包含它们的方法时,对象被收集的机会会更高。
例如:
public class Main {
public static void main(String[] args) throws Exception {
ReferenceQueue<Class<?>> queue = new ReferenceQueue<>();
WeakReference<Class<?>> ref = f(queue);
do System.gc(); while(queue.remove(1000) != ref);
System.out.println("class has been collected");
}
public static
WeakReference<Class<?>> f(ReferenceQueue<Class<?>>queue) throws Exception {
URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
try(URLClassLoader cl=new URLClassLoader(new URL[]{ url }, null)) {
Class<?> c = cl.loadClass("com.expirement.Loadable1");
return new WeakReference<>(c, queue);
}
}
}