让我们看看这段代码来了解类加载器是如何泄漏的
Main.java
public class Main {
public static void main(String...args) throws Exception {
List<Object> list = new ArrayList<>();
loadClass(list);
while (true) {
System.gc();
Thread.sleep(1000);
}
}
private static void loadClass(List list) throws Exception {
URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
MyCustomClassLoader cl = new MyCustomClassLoader(url);
Class<?> clazz = cl.loadClass("com.test.Foo");
list.add(clazz.newInstance());
cl = null;
}
}
class MyCustomClassLoader extends URLClassLoader {
public MyCustomClassLoader(URL... urls) {
super(urls, null);
}
@Override
protected void finalize() {
System.out.println("*** CustomClassLoader finalized!");
}
}
Foo.java
public class Foo {
public Foo() {
System.out.println("Test ClassLoader: " + this.getClass().getClassLoader());
}
@Override
protected void finalize() {
System.out.println( this + " finalized!");
}
}
这个的输出如下:
测试类加载器:com.test.MyCustomClassLoader@71dac704
所以,在这里我们可以看到“*** CustomClassLoader finalized!”没有被调用,这是因为 MyCustomClassLoader 持有对象列表的引用,因为类加载器加载的实例保存在其中。
现在,让我们稍微修改一下代码,所以这里我们将 list 设置为 null
public static void main(String...args) throws Exception {
List<Object> list = new ArrayList<>();
loadClass(list);
while (true) {
System.gc();
Thread.sleep(1000);
list = null;
}
}
现在看看输出
测试类加载器:com.test.MyCustomClassLoader@71dac704
com.test.Foo@650de12 敲定!
*** CustomClassLoader 完成!