ThreadLocal真的存在内存泄漏吗?来这里探讨真相吧( 二 )


所以平时我们线程使用完销毁是不会有有内存泄漏问题的
那么网上一直流传的内存泄漏问题是否真的存在呢?其实是存在的 。大家想想我们平时在做项目的时候自己创建线程的场景多吗?很明显基本没有,因为线程的创建销毁代价太过昂贵,我们一般都是使用线程池,那么问题来了,线程池的线程一般会循环利用,不会销毁 。所以网上说的内存泄漏是发生在使用线程池的场景
再谈弱引用问题
我们首先来看看的set源码
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}}
短短几行,其实也很简单,就是获取到线程的然后set一个属性,然后我们看看
方法
private void set(ThreadLocal key, Object value) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal k = e.get();if (k == key) {e.value = http://www.kingceram.com/post/value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz>= threshold)rehash();}
我们看新加值的关键代码
tab[i] = new Entry(key, value);
可以看到Entry的key 就是我们的,在看Entry的定义
static class Entry extends WeakReference> {}
这里就看到了我们说的弱引用,即Entry的key是对的弱引用,那么现在的引用状态呢,这里借鉴网上的一张图:
可以很清晰的看到Entry对的弱引用 。
注意这里弱引用解决的问题:首先虽然Key是对,但是key并不会被回收,因为线程对他是强引用,这里解决的问题是为了防止自身的内存泄漏,因为如果这里key对是强引用,那么在我们将栈中的变量不指向,也无法被回收,因为还有key对他是一个强引用,如果这是弱引用,就可以顺利被回收
弱引用:使用修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收
这里证明比较复杂,但我还是引用网上的一段代码去证明
首先证明key 被回收的场景
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {Thread t = new Thread(()->test("abc",false));t.start();t.join();System.out.println("--gc后--");Thread t2 = new Thread(() -> test("def", true));t2.start();t2.join();}private static void test(String s,boolean isGC){try {new ThreadLocal<>().set(s);if (isGC) {System.gc();}Thread t = Thread.currentThread();Class clz = t.getClass();Field field = clz.getDeclaredField("threadLocals");field.setAccessible(true);Object ThreadLocalMap = field.get(t);Class tlmClass = ThreadLocalMap.getClass();Field tableField = tlmClass.getDeclaredField("table");tableField.setAccessible(true);Object[] arr = (Object[]) tableField.get(ThreadLocalMap);for (Object o : arr) {if (o != null) {Class entryClass = o.getClass();Field valueField = entryClass.getDeclaredField("value");Field referenceField = entryClass.getSuperclass().getSuperclass().getDeclaredField("referent");valueField.setAccessible(true);referenceField.setAccessible(true);System.out.println(String.format("弱引用key:%s,值:%s", referenceField.get(o), valueField.get(o)));}}} catch (Exception e) {e.printStackTrace();}}
输出结果:
弱引用key:java.lang.ThreadLocal@433619b6,值:abc弱引用key:java.lang.ThreadLocal@418a15e3,值:java.lang.ref.SoftReference@bf97a12--gc后--弱引用key:null,值:def