背景
对ThreadLocal的实际使用场景一直有点模糊。在code review中大家对ThreadLocal是否会出现内存泄漏问题提出不同看法。故上网一探究竟,但是发现网上的说法不一,有的说会导致内存泄漏有的说不会,很难发现实战的结晶。
分析
结构
一个简洁的ThreadLocal类的内部结构如下
public class ThreadLocal<T> { static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal> { Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } private ThreadLocal.ThreadLocalMap.Entry[] table; } } }
ThreadLocal类中定义了一个静态内部类ThreadLocalMap,ThreadLocalMap并没有实现Map接口,而是自己"实现"了一个Map,在ThreadLocalMap内部定义了一个静态内部类Entry继承自WeakReference,寻找一下对WeakReference的记忆—当所引用的对象在JVM内不再有强引用指向时,GC后weak reference将会被自动回收。
流程
然后,我们从创建的流程来看一下
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方法,并不能获取到ThreadLocalMap,于是ThreadLocalMap被创建
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap(ThreadLocal firstKey, Object firstValue){ table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
可以看到ThreadLocalMap以当前ThreadLocal对象为key被创建,其内部存储结构如上,将key进行hash计算后,再将key和value放入Entry中,
注意一下上面t.threadLocals = new ThreadLocalMap(this, firstValue),实际上是一个Thread的成员变量在引用着这个ThreadLocalMap如下
public class Thread{ ThreadLocal.ThreadLocalMap threadLocals = null; }所以我们可以分析,当Thread运行结束后(没有线程池):
- 这个ThreadLocalMap对象会被GC回收
- ThreadLocalMap的成员变量table所指向的对象会被gc回收,这时注意Entry是继承了WeakReference的,所以Entry对象也会被gc回收
- value作为Entry的成员变量自然也会被gc回收
结论
这样看来,较为严谨的说法是,在不使用线程池的前提下,即使不调用remove方法,线程的"变量副本"也会被gc回收,即不会造成内存泄漏的情况。
问题
1、那在使用线程池的情况下呢?会不会出现内存泄漏的问题呢?我做了这样一个简单的小测试
public static void testThreadLocalExist(){ ExecutorService service = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { if(i == 0){ service.execute(new Runnable() { public void run() { System.out.println("Thread id is " + Thread.currentThread().getId()); threadLocal.set("variable"); } }); } else if(i > 0){ service.execute(new Runnable() { public void run() { if("variable".equals(threadLocal.get())){ System.out.println("Thread id " + Thread.currentThread().getId() + " got it !"); } } }); } } } 输出: Thread id is 9 Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it !如上测试,我初始化了一个线程数量为1的线程池,为了保证每次线程池中获取到的都是同一个线程
相关推荐
问题背景在 Tomcat 中,下面的代码都在 webapp 内,会导致 WebappClassLoaderWebappClassLoader 泄漏,无法被回收。
理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal
正确理解ThreadLocal.pdf
主要介绍了深入理解ThreadLocal工作原理及使用示例,涉及ThreadLocal<T> 简介和使用示例及ThreadLocal的原理等相关内容,具有一定参考价值,需要的朋友可以了解下。
一篇文章我们来分析一个Java中ThreadLocal内存泄露的案例。分析问题的过程比结果更重要,理论结合实际才能彻底分析出内存泄漏的原因。
ThreadLocal应用示例及理解,这个写了相关的示例,可以参考一下。
NULL 博文链接:https://liuinsect.iteye.com/blog/1827012
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量
04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、...
并且由于每个线程在访问该变量时,读取和修改的,都是独有的那份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。} catch (SQL
JVM的基础和调优【JMM 内存结构 GC OOM 性能调优 ThreadLocal】 内存泄露:是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄露, 一次的内存泄露似乎不会有大的影响,但是内存泄露堆积的后果就是内存...
声明仅作学习。如有不适,请告知。清晰的看到一个线程Thread中存在一个ThreadLocalMap,ThreadLocalMap中的key对应ThreadLo
ThreadLocal中内存泄漏和数据丢失问题的问题浅析及解决方案.docx
主要介绍了Android 中ThreadLocal的深入理解的相关资料,希望通过本文能帮助到大家,让大家理解应用ThreadLocal,需要的朋友可以参考下
主要介绍了ThreadLocal原理及内存泄漏原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
ThreadLocal入门教程。 讲解了线程安全和ThreadLocal的使用的基本知识。
ThreadLocal
深入研究java.lang.ThreadLocal类。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是 threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。