ThreadLocal类第一次使用导致内存溢出
Posted daiveking
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal类第一次使用导致内存溢出相关的知识,希望对你有一定的参考价值。
ThreadLocal类的理解
问题来源
前段时间开发一个新任务,发现一个问题,生产环境业务中读取会员信息,出现读到其他会员的现象,就是同一个会员ID能查出多个会员信息,一开始并没有打日志,我怀疑是数据问题,产品说ID是唯一的,于是我打了日志,发现会员信息有时候正常,有时候不正常,在单机测试环境下没有问题,但是我并没有打印直接返回的对象,我开始怀疑是不是并发的问题,我在想是不是ThreadLocal的问题,我问了之前开发的同事,他们说大家都这么用不可能有问题,我再次把日志打的更加详细,把缓存查询的所有对象信息全部打出来,发现问题了,跟之前现象一样,时而读到其他用户信息,时而读到该会员的信息,我确认就是ThreadLocal的问题,我网上搜了一下,果然是TreadLocal内存泄漏问题,使用完ThreadLocal,没有调用remove方法,导致新的线程拿到线程池中未销毁的线程的会员信息,还好功能影响不大,否则要背大锅了,也是第一次使用ThreadLocal,写篇文章加深记忆。
ThreadLocal类是什么
从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
从字面意思来看非常容易理解,但是从实际使用的角度来看,就没那么容易了,作为一个面试常问的点,使用场景那也是相当的丰富:
1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束(我们就是为了避免在方法调用中来回传递参数,把大量查询出来的对象放在ThreadLocal)。
2、线程间数据隔离
3、进行事务操作,用于存储线程事务信息。
4、数据库连接,Session会话管理。
现在相信你已经对ThreadLocal有一个大致的认识了,下面我们看看如何用?
ThreadLocal类是怎么用
最简单的例子(网上很多人说get方法报空指针,我直接测试不会抛出异常):
打印结果:
ThreadLocal1:null
ThreadLocal2:ThreadLocal test
场景例子:
可以看出:使用非常简单,只要基于本线程的数据,都可以保存在ThreadLocal中
ThreadLocal源码理解
get方法:代码很简单,直接获取该线程为key的TreadLocalMap的值
Set方法:
Remove方法:
TreadLocalMap的定义:
我们可以看到ThreadLocalMap其实就是ThreadLocal的一个静态内部类,里面定义了一个Entry来保存数据,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value
其实TreadLocalMap 是TreadLocal 的一个内部静态类, TreadLocalMap就是用来存储数据的对象,实现跟HashMap类似。
OK,其实内部源码很简单,现在我们总结一波
(1)每个Thread维护着一个ThreadLocalMap的引用
(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
(5)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
OK,现在从源码的角度上不知道你能理解不,对于ThreadLocal来说关键就是内部的ThreadLocalMap。
ThreadLocal内存溢出分析
只要是介绍ThreadLocal的文章都会帮大家认识一个点,那就是内存泄漏问题。我们先来看下面这张图。
上面这张图详细的揭示了ThreadLocal和Thread以及ThreadLocalMap三者的关系。
1、Thread中有一个map,就是ThreadLocalMap
2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。
3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收
4、重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。
以上是关于ThreadLocal类第一次使用导致内存溢出的主要内容,如果未能解决你的问题,请参考以下文章