分析ThreadLocal内存泄漏问题

 更新时间:2023年07月12日 11:30:53   作者:小小小小明明明明明明明明  
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量传递的复杂度,但是如果滥用ThreadLocal可能会导致内存泄漏,所以本文将为大家分析ThreadLocal内存泄漏问题

ThreadLocal的实现原理

ThreadLocal的实现:

每一个Thread内部维护一个ThreadLocalMap映射表,这个映射表的keyThreadLocal实例本身,value是真正需要存储的Object

也就是说ThreadLocal本身不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value的。但是ThreadLocalMap是使用ThreadLocal的弱引用作为key的,弱引用的对象在GC时会被回收。

ThreadLocal为什么会内存泄漏

ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统GC的时候,这个ThreadLocal会被回收,这样一来,ThreadLocalMap中会出现keynullEntry,这样就没有办法访问keynullEntryvalue,如果当前线程迟迟不结束,这些keynullEntryvalue就会存在一条强引用链,永远无法回收,造成内存泄漏。

其实ThreadLocal的设计中已经考虑到了这种情况,也加上了一些预防措施,在调用getsetremove方法的时候,会清楚线程ThreadLocalMap里所有keynullvalue

但是这些被动的预防措施并不能保证不会内存泄漏:

  • 使用staticThreadLocal,延长了ThreadLocal的生命周期,可能导致的内存泄漏。
  • 分配使用了ThreadLocal又不再调用get() ,set() ,remove() 方法,那么就会导致内存泄漏。

为什么使用弱引用

从表面上看内存泄漏的根本原因是使用了弱引用,那么为什么使用弱引用而不使用强引用呢?下面看看官方文档的说法:

To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.

翻译过来就是:为了应对非常大和长时间的用途,哈希表使用弱引用。

下面我们分两种情况讨论:

  • key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
  • key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,getremove的时候会被清除。

比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用 ThreadLocal 不会内存泄漏,对应的 value 在下一次 ThreadLocalMap 调用 set , get , remove 的时候会被清除

因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

ThreadLocal最佳实践

综合上面的分析,我们可以理解ThreadLocal内存泄漏的前因后果,那么怎么避免内存泄漏呢?

  • 每次使用完ThreadLocal,都调用它的remove() 方法,清除数据。

在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。

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

相关文章

  • Java Stream流之求和的实现

    Java Stream流之求和的实现

    这篇文章主要介绍了Java Stream流之求和的实现,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Java开启JMX远程监控服务配置

    Java开启JMX远程监控服务配置

    这篇文章主要为大家介绍了Java开启JMX远程监控的服务配置,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • 数据库CURD必备搭档mybatis plus详解

    数据库CURD必备搭档mybatis plus详解

    这篇文章主要为大家介绍了数据库CURD必备搭档mybatis plus详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Java中文件写入内容的几种常见方法

    Java中文件写入内容的几种常见方法

    本文主要介绍了Java中文件写入内容的几种常见方法,主要包括使用NIO的Files工具类、通过commons-io的FileUtils工具类、RandomAccessFile、PrintWriter和BufferedWriter这几种,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • Spring Bean注册与注入实现方法详解

    Spring Bean注册与注入实现方法详解

    首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入。依赖注入是指:让调用类对某一接口的实现类的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖。Spring容器中支持的依赖注入方式主要有属性注入、构造函数注入、工厂方法注入
    2022-10-10
  • Mybatis SqlSession案例详解

    Mybatis SqlSession案例详解

    这篇文章主要介绍了Mybatis SqlSession详解,本文我们讲了如何创建SqlSession的几个步骤,最后我们获得一个DefaultSqlSession对象,里面包含了执行器Executor和配置对象Configuration,需要的朋友可以参考下
    2023-04-04
  • JavaWeb实现自动登录功能

    JavaWeb实现自动登录功能

    这篇文章主要为大家详细介绍了JavaWeb实现自动登录功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • 深入理解Java设计模式之适配器模式

    深入理解Java设计模式之适配器模式

    这篇文章主要介绍了JAVA设计模式之适配器模式的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解
    2021-11-11
  • JAVA操作elastic search的详细过程

    JAVA操作elastic search的详细过程

    Elasticsearch Rest High Level Client 是 Elasticsearch 官方提供的一个 Java 客户端库,用于与 Elasticsearch 进行交互,本文介绍JAVA操作elastic search的详细过程,感兴趣的朋友一起看看吧
    2024-08-08
  • Mybatis映射文件根标签与子标签示例讲解

    Mybatis映射文件根标签与子标签示例讲解

    这篇文章主要介绍了Mybatis映射文件根标签与子标签,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01

最新评论