ThreadLocal真的会造成内存泄漏吗?

简介: ThreadLoca在并发场景中,应用非常多。前几天有位小伙伴问我一个问题,说ThreadLocal是不是真的会造成内存泄漏?今天给大家做一个分享,个人见解,仅供参考。如果大家有其他见解可以在评论区讨论。

【Java面试】并发场景中,ThreadLocal会造成内存泄漏吗?


ThreadLoca在并发场景中,应用非常多。前几天有位小伙伴问我一个问题,说ThreadLocal是不是真的会造成内存泄漏?今天给大家做一个分享,个人见解,仅供参考。如果大家有其他见解可以在评论区讨论。


【导航条:基本原理】


(转场,屏幕黑底白字,正中央出现“1 ThreadLocal的基本原理”)


1、ThreadLocal的基本原理


考虑到很多小伙伴可能还不太了解ThreadLocal,我先简单介绍一下ThreadLocal。在多线程并发访问同一个共享变量的情况下,如果不做同步控制的话,就可能会导致数据不一致的问题,所以,我们需要使用synchronized加锁来解决。


而ThreadLocal换了一个思路来处理多线程的情况,


(开始播放动画)  

acf33103617f7299cffe0e529bd727c2.png

ThreadLocal本身并不存储数据,它使用了线程中的threadLocals属性,threadLocals的类型就是在ThreadLocal中的定义的ThreadLocalMap对象,当调用ThreadLocal的set(T value)方法时,ThreadLocal将自身的引用也就是this作为Key,然后,把用户传入的值作为Value存储到线程的ThreadLocalMap中,这就相当于每个线程的读写操作都是基于线程自身的一个私有副本,线程之间的数据是相互隔离的,互不影响。


(结束播放动画)  


这样一来基于ThreadLocal的操作也就不存在线程安全问题了。它相当于采用了用空间来换时间的思路,从而提高程序的执行效率。


【导航条:四种对象引用】


(转场,屏幕黑底白字,正中央出现“2 四种对象引用”)


2、四种对象引用


在ThreadLocalMap内部,维护了一个Entry数组table的属性,用来存储键值对的映射关系,来看这样一段代码片段:


(开始播放动画)  

ddcc6ea1e0878f53b273a2d4a58bc8d6.png

static class ThreadLocalMap {
    ...
    private Entry[] table;
    static class Entry implements WeakReference<ThreadLocal<?>> {
    Object value;
            Entry(ThreadLocal<?> k, Object v) {
                 super(k);
                 value = v;
            }
    }
    ...
}

(结束播放动画)  


Entry将ThreadLocal作为Key,值作为Value保存,它继承自WeakReference,注意构造函数里的第一行代码super(k),这意味着ThreadLocal对象是一个「弱引用」。有的小伙伴可能对「弱引用」不太熟悉,这里再介绍一下Java的四种引用关系。


在JDK1.2之后,Java对引用的概念做了一些扩充,将引用分为“强”、“软”、“弱”、“虚”四种,由强到弱依次为:


(开始播放动画)  

96f8f8a772c28a655be18f4fd49548e8.png

强引用:指代码中普遍存在的赋值行为,如:Object o = new Object(),只要强引用关系还在,对象就永远不会被回收。

   软引用:还有用处,但不是必须存活的对象,JVM会在内存溢出前对其进行回收,例如:缓存。

   弱引用:非必须存活的对象,引用关系比软引用还弱,不管内存是否够用,下次GC一定回收。

   虚引用:也称“幽灵引用”、“幻影引用”,最弱的引用关系,完全不影响对象的回收,等同于没有引用,虚引用的唯一的目的是对象被回收时会收到一个系统通知。


(结束播放动画)  


这个描述还是比较官方的,简单总结一下,大家应该都追过剧,强引用就好比是男主角,怎么都死不了。软引用就像女主角,虽有一段经历,还是没走到最后。弱引用就是男二号,注定用来牺牲的。虚引用就是路人甲了。


【导航条:内存泄漏原因】


(转场,屏幕黑底白字,正中央出现“3 造成内存泄漏的原因”)


3、造成内存泄漏的原因


内存泄漏和ThreadLocalMap中定义的Entry类有非常大的关系。


(开始展示动画)  

3b28e954d24ef84a069452d8bb0c6db2.png

这个动画完整地展示了ThreadLocal中对象引用的关系,需要这张高清图的小伙伴可以在评论区留言。


由于ThreadLocal对象是弱引用,如果外部没有强引用指向它,它就会被GC回收,导致Entry的Key为空(null),如果这时Value外部也没有强引用指向它,那么Value就永远也访问不到了,按理也应该被GC回收,但是由于Entry对象还在强引用Value,导致Value无法被回收,这时「内存泄漏」就发生了,Value成了一个永远也无法被访问,但是又无法被回收的对象。


(结束展示动画)  


Entry对象属于ThreadLocalMap,ThreadLocalMap又属于Thread,如果线程本身的生命周期很短,短时间内就会被销毁,那么「内存泄漏」立刻就会得到解决,只要线程被销毁,Value也会随之被回收。


问题是,线程本身是非常珍贵的计算机资源,很少会去频繁的创建和销毁,一般都是通过线程池来使用,这就将线程的生命周期大大拉长,「内存泄漏」的影响也会越来越大。


最后,一句话总结一下。


(开始展示动画)  

6196d962953c3dda113795ed3e5e8d79.png

threadLocals对象中的Entry对象不再使用后,如果没有及时清除Entry对象 ,而程序自身也无法通过垃圾回收机制自动清除,就可能导致内存泄漏。


(结束展示动画)  


【导航条:如何避免】


(转场,屏幕黑底白字,正中央出现“4 如何避免内存泄漏?”)


4、如何避免内存泄漏?


不要听到「内存泄漏」就不敢使用ThreadLocal,只要规范化使用是不会有问题的。我给大家支几个招:


(开始展示动画)  

2a194ba49ed707f63c324cc31b419552.png

1、每次使用完ThreadLocal都记得调用remove()方法清除数据。


2、将ThreadLocal变量尽可能地定义成static final,避免频繁创建ThreadLocal实例。这样也就保证程序一直存在ThreadLocal的强引用,也能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的Value值,进而清除掉。



(结束展示动画)  


当然,就是使用不规范,ThreadLocal内部也做了一些优化,比如:


(开始展示动画)  

2960dd54f24b35e85bff52295e128943.png

1、调用set()方法时,ThreadLocal会进行采样清理、全量清理,扩容时还会继续检查。


2、调用get()方法时,如果没有直接命中或者向后环形查找时也会进行清理。


3、调用remove()时,除了清理当前Entry,还会向后继续清理。


所以,我真的希望小伙伴们,不要被制造焦虑的面试官们卷得太深。听懂的小伙伴,请关注点个赞,下次不迷路。


扫描下方二维码即可免费领取需要更多干货内容,还有海量面试资料,只弹干货不惨水!


本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

相关文章
|
4月前
|
存储 Java 数据挖掘
来聊聊ThreadLocal内存泄露分析
来聊聊ThreadLocal内存泄露分析
|
5月前
|
机器学习/深度学习 安全 Java
从源码深入详解ThreadLocal内存泄漏问题
从源码深入详解ThreadLocal内存泄漏问题
81 0
|
6月前
|
存储 安全 Java
表妹和我纠结,线上系统因为一个ThreadLocal直接内存飙升
大家对于ThreadLocal这一个都应该听说过的吧,不知道大家对于这个掌握的怎么样了已经 这不,我那爱学习的表妹不知道又从哪里听来了这个技术点,回家就得意洋洋地给我说,表哥,我今天又学会了一个技术点ThreadLocal
|
7月前
|
存储 Java
【ThreadLocal的内存泄露问题】
【ThreadLocal的内存泄露问题】
|
8月前
|
Java
ThreadLocal内存泄露分析
ThreadLocal内存泄露分析
57 0
|
8月前
|
存储 机器学习/深度学习 缓存
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
|
10月前
|
Java
【Java面试】说说你对ThreadLocal内存泄漏问题的理解
【Java面试】说说你对ThreadLocal内存泄漏问题的理解
90 0
|
10月前
|
缓存 监控 Java
【Java面试】Java中都有那些引用类型?(关于弱引用是如何解决ThreadLocal内存泄漏问题)
【Java面试】Java中都有那些引用类型?(关于弱引用是如何解决ThreadLocal内存泄漏问题)
66 0
|
10月前
|
Java
ThreadLocal-内存泄露问题
在使用ThreadLocal时,如果没有及时清理ThreadLocal变量,就可能会导致内存泄漏问题。这是因为ThreadLocalMap中的Entry对象持有了对ThreadLocal对象的强引用,而ThreadLocal对象又持有了对变量副本的引用。如果没有手动调用remove()方法来清理ThreadLocal变量,那么Entry对象和变量副本就会一直存在于ThreadLocalMap中,无法被垃圾回收。
62 0
|
10月前
|
存储 Java 数据库连接
深入理解Java ThreadLocal及其内存泄漏防范
一、ThreadLocal简介 在Java中,ThreadLocal是一种线程封闭的机制,其主要目的是为每个线程都创建一个单独的变量副本。这意味着,每个线程都可以独立地改变自己的副本,而不会影响其他线程的副本。 ThreadLocal常被用于解决多线程编程中的数据同步问题。例如,我们可以用ThreadLocal来保存数据库连接、Session等常见的线程不安全的变量。
534 0