1回复
6月前
这个 ThreadLocal 怎么防止类加载器被回收?
我想搞清楚 Threadlocal是怎样导致类加载器泄漏的,所以写了这些代码
public class Main {
public static void main(String... args) throws Exception {
loadClass();
while (true) {
System.gc();
Thread.sleep(1000);
}
}
private static void loadClass() throws Exception {
URL url = Main.class.getProtectionDomain()
.getCodeSource()
.getLocation();
MyCustomClassLoader cl = new MyCustomClassLoader(url);
Class<?> clazz = cl.loadClass("com.test.Foo");
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 {
private static final ThreadLocal<Bar> tl = new ThreadLocal<Bar>();
public Foo() {
Bar bar = new Bar();
tl.set(bar);
System.out.println("Test ClassLoader: " + this.getClass()
.getClassLoader());
}
@Override
protected void finalize() {
System.out.println(this + " finalized!");
}
}
Bar.java
public class Bar {
public Bar() {
System.out.println(this + " created");
System.out.println("Bar ClassLoader: " + this.getClass()
.getClassLoader());
}
@Override
public void finalize() {
System.out.println(this + " finalized");
}
}
我运行代码后,它显示未调用 MyCustomClassloader 和 Bar finalize,仅调用 Foo finalize。 但我把 Threadlocal 更改为 String 时,所有的 finalize 都会被调用。
public class Foo {
private static final ThreadLocal<String> tl = new ThreadLocal<String>();
public Foo() {
Bar bar = new Bar();
tl.set("some");
System.out.println("Test ClassLoader: " + this.getClass()
.getClassLoader());
}
有大佬能解释一下为什么在使用 ThreadLocal 作为 String 和 Bar 的时候会有区别吗?
1009 阅读
1. 并不是ThreadLocal防止回收,而是ThreadLocal的机制建立起了引用。
2. 并不是String和Bar有区别,而是你本来在ThreadLocal里设置了Bar的对象,那么bar就被引用了,进而加载bar的classloader也被引用了,那么他们就是存活的,而foo并未被引用;而你改成设置一个string以后引用的就是这个string,而不是bar,那么classloader也未被引用了,就都可以被回收了。