求助>这个 ThreadLocal 怎么防止类加载器被回收?>
1回复

这个 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 的时候会有区别吗?

691 阅读
请先登录,再评论

1. 并不是ThreadLocal防止回收,而是ThreadLocal的机制建立起了引用。
2. 并不是String和Bar有区别,而是你本来在ThreadLocal里设置了Bar的对象,那么bar就被引用了,进而加载bar的classloader也被引用了,那么他们就是存活的,而foo并未被引用;而你改成设置一个string以后引用的就是这个string,而不是bar,那么classloader也未被引用了,就都可以被回收了。

24周前