ThreadLocal的内存泄露问题

 更新时间:2023年03月30日 09:46:06   作者:小孙的Blog  
这篇文章主要介绍了Java中ThreadLocal的内存泄露问题,以及为什么会出现内存泄漏,感兴趣的小伙伴可以参考阅读

ThreadLocal的内部实现

在每一个线程Thread对象中,都维护了一个ThreadLocalMap对象。

 ThreadLocalMap中又维护了一个k v 形式的Entry对象,key指向了当前ThreadLocal对象,value就是我们实际在ThreadLocal中存储的值。

注意,这里的Entry中的key存放是ThreadLocal的弱引用。

 实现指的是强引用,虚线指的是弱引用。

 其实际上,ThreaLocal本身是不存储值的,我们在使用其对应的set、get方法时,都是操作的其对应的ThreadLocalMap对象。

为什么会出现内存泄露?

从上述可以看到,在Entry中的key存储的ThreadLocal的弱引用

弱引用在发生GC时,就会被垃圾回收掉,具体可以参考JVM相关的知识。

所以,在当前线程正在运行的时候,发生GC时,在ThreadLocal对象没有被其它地方强引用时,key指向ThreadLocal的虚引用就会立即断开(被垃圾回收掉),这时,就会出现ThreadLocalMap中存在key为null的Entry,只要当前线程不结束,该ThreadLocalMap对象就会一直存在,永远无法回收,因为此时还存在一条强引用的链路,从图中也可以发现:

Current Thread Reference --> Current Thread --> ThreadLocalMap --> EntryValue --> Object

所以这个时候就造成了内存泄露。

Entry对象的key为什么要使用弱引用,有什么好处?

在上述所说的问题中,即使ThreadLocalMap中存在key为null的Entry,但是该Entry的value值并不会因为GC而被回收(value存本身就存着一个强引用的对象),所以就导致了该对象不会被回收掉而出现了内存泄露。

其实,ThreadLocalMap在设计时就考虑到了这个方面,它也采取了一些措施来避免这种key为null,而value不为null的对象占用内存,在我们调用ThreadLocal的set、get、remove方法时,都会将这些key为null的对象清空掉,避免因为这种情况而导致内存泄露。

这也就是为什么key要存储弱引用的原因。

假设如果存储的强引用,我们断开ThreadLocal Reference —> ThreadLocal的引用,会发现key强引用了ThreadLocal,导致该对象永远无法被GC。

但是,即使上述提供了避免内存泄露的措施,但是不能完全避免,比如以下的情况:

  • 分配了ThreadLocal对象,但是并没有执行其get、set、remove方法,导致不能有效的清除null对象;
  • 使用线程池的情况下,使用完ThreadLocal一定要使用remove方法即时清理,因为ThreadLocal是属于某个线程的,而在使用线程池的情况下,这些线程都是可重复利用、存活时间长的线程,如果在使用过程中不仅从即使的remove,那么不仅会造成内存泄露的问题,还会引发一些功能逻辑问题,比如,B请求可能和A请求分配到了线程池中的同一个线程,那么它们拿到的ThreadLocal就是一样的。

 set

cleanSomeSlots

get

关于弱引用的一些知识补充

学习的过程中想到了一个问题,弱引用会不会导致运行过程中GC清除key,导致找不到对应的value?

可能是当时对弱引用的理解不够熟,所以产生了这个问题,如下面的代码。

public class TestDemo {

    static ThreadLocal threadLocal = new ThreadLocal();

    public static void main(String[] args) {

        threadLocal.set("demo");
        System.gc();
        System.out.println(threadLocal.get());	// demo

    }
}

为什么还获取到值,不是说在发现一次GC,弱引用就会被清除掉吗?

糊涂了。

弱引用只有在该对象没有被其它地方强引用的时候,才会被GC。

上述的原因就是因为,很明显,ThreadLocal对象除了被key弱引用,还由一个Reference强引用指向它,所以肯定不会被GC。

如果是这样,那下一次GC,这个对象就被干掉了。

举一个简单的例子,帮助理解:

WeakReference<User> userWeakReference = new WeakReference<User>(new User("jack"));

System.out.println(userWeakReference.get() == null);  // false

System.gc();

System.out.println(userWeakReference.get() == null);  // true

很明显,GC的时候直接清除了这个弱引用对象。

 userWeakReference.get(), 如果此方法为空, 那么说明weakReference指向的对象已经被回收了。

WeakReference<User> userWeakReference = new WeakReference<User>(new User("jack"));
User jack = userWeakReference.get();
System.out.println(userWeakReference.get() == null);  // false

System.gc();

System.out.println(userWeakReference.get() == null);  // false

 当我们添加了一个强引用来指向它的时候,该对象并不会被gc清除(弱引用还在)。

 到此这篇关于ThreadLocal的内存泄露问题的文章就介绍到这了,更多相关ThreadLocal内存泄露内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中SpringCloud的五大组件详解

    Java中SpringCloud的五大组件详解

    这篇文章主要介绍了Java中SpringCloud的五大组件详解,Spring cloud是一个基于Spring Boot实现的服务治理工具包,在微服务架构中用于管理和协调服务,需要的朋友可以参考下
    2023-07-07
  • Spring MVC实现GET请求接收Date类型参数

    Spring MVC实现GET请求接收Date类型参数

    这篇文章主要介绍了Spring MVC实现GET请求接收Date类型参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • java同步与异步的学习笔记整理

    java同步与异步的学习笔记整理

    在本篇文章里小编给大家整理了关于java同步与异步的学习笔记整理内容,需要的朋友们参考下。
    2020-02-02
  • java中拼接字符串的5种方法效率对比

    java中拼接字符串的5种方法效率对比

    这篇文章主要给大家介绍了关于java中拼接字符串的5种方法效率对比的相关资料,文中通过示例代码和图片介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • Java基础知识精选 你答对了几道?

    Java基础知识精选 你答对了几道?

    精选Java基础知识讲解,看看你能答对多少?
    2017-09-09
  • 关于log4j日志扩展---自定义PatternLayout

    关于log4j日志扩展---自定义PatternLayout

    这篇文章主要介绍了关于log4j日志扩展---自定义PatternLayout,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • JAVA 数据结构之Queue处理实例代码

    JAVA 数据结构之Queue处理实例代码

    这篇文章主要介绍了JAVA 数据结构之Queue处理实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • Spring多个数据源配置详解

    Spring多个数据源配置详解

    在实际场景中,会有需要配置多个数据源的场景,本文就介绍一下如何配置Spring多数据源,具有一定的参考价值,感兴趣的可以了解一下
    2021-08-08
  • Java线程让步_动力节点Java学院整理

    Java线程让步_动力节点Java学院整理

    yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权。下面通过本文给大家介绍Java线程让步的相关知识,需要的朋友参考下吧
    2017-05-05
  • SpringCloud之loadbalancer负载均衡组件实战详解

    SpringCloud之loadbalancer负载均衡组件实战详解

    LoadBalancer是Spring Cloud官方提供的负载均衡组件,可用于替代Ribbon,这篇文章主要介绍了SpringCloud之loadbalancer负载均衡组件,需要的朋友可以参考下
    2023-06-06

最新评论