ThreadLocal 内存泄露的原因及处理方式

news/2024/10/20 11:51:18/

1、ThreadLocal 使用原理
ThreadLocal的主要用途是实现线程间变量的隔离,表面上他们使用的是同一个ThreadLocal, 但是实际上使用的值value却是自己独有的一份。用一图直接表示threadlocal 的使用方式。
在这里插入图片描述
从图中我们可以当线程使用threadlocal 时,是将threadlocal当做当前线程thread的属性ThreadLocalMap 中的一个Entry的key值,实际上存放的变量是Entry的value值,我们实际要使用的值是value值。value值为什么不存在并发问题呢,因为它只有一个线程能访问。threadlocal我们可以当做一个索引看待,可以有多个threadlocal 变量,不同的threadlocal对应于不同的value值,他们之间互不影响。ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享
2、ThreadLocal 内存泄露的原因

Entry将ThreadLocal作为Key,值作为value保存,它继承自WeakReference,注意构造函数里的第一行代码super(k),这意味着ThreadLocal对象是一个「弱引用」。

static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

主要两个原因
1 . 没有手动删除这个 Entry
2 . CurrentThread 当前线程依然运行

    第一点很好理解,只要在使用完下 ThreadLocal ,调用其 remove 方法删除对应的 Entry ,就能避免内存泄漏。第二点稍微复杂一点,由于ThreadLocalMap 是 Thread 的一个属性,被当前线程所引用,所以ThreadLocalMap的生命周期跟 Thread 一样长。如果threadlocal变量被回收,那么当前线程的threadlocal 变量副本指向的就是key=null, 也即entry(null,value),那这个entry对应的value永远无法访问到。实际私用ThreadLocal场景都是采用线程池,而线程池中的线程都是复用的,这样就可能导致非常多的entry(null,value)出现,从而导致内存泄露。

综上, ThreadLocal 内存泄漏的根源是:
由于ThreadLocalMap 的生命周期跟 Thread 一样长,对于重复利用的线程来说,如果没有手动删除(remove()方法)对应 key 就会导致entry(null,value)的对象越来越多,从而导致内存泄漏.

3、 为什么不将key设置为强引用
3.1 、key 如果是强引用
那么为什么ThreadLocalMap的key要设计成弱引用呢?其实很简单,如果key设计成强引用且没有手动remove(),那么key会和value一样伴随线程的整个生命周期。

1、假设在业务代码中使用完ThreadLocal, ThreadLocal ref被回收了,但是因为threadLocalMap的Entry强引用了threadLocal(key就是threadLocal), 造成ThreadLocal无法被回收。在没有手动删除Entry以及CurrentThread(当前线程)依然运行的前提下, 始终有强引用链CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> entry, Entry就不会被回收( Entry中包括了ThreadLocal实例和value), 导致Entry内存泄漏也就是说: ThreadLocalMap中的key使用了强引用, 是无法完全避免内存泄漏的。请结合图1看。

3.3 那么为什么 key 要用弱引用
事实上,在 ThreadLocalMap 中的set/getEntry 方法中,会对 key 为 null(也即是 ThreadLocal 为 null )进行判断,如果为 null 的话,那么会把 value 置为 null 的.这就意味着使用threadLocal , CurrentThread 依然运行的前提下.就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用的 ThreadLocal 会被回收.对应value在下一次 ThreadLocaI 调用 get()/set()/remove() 中的任一方法的时候会被清除,从而避免内存泄漏.

3.4 如何正确的使用ThreadLocal
1、将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露

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


http://www.ppmy.cn/news/48385.html

相关文章

使用MyBatis实现简单查询

文章目录 一&#xff0c;创建数据库与表&#xff08;一&#xff09;在Navicat里创建MySQL数据库testdb&#xff08;二&#xff09;创建用户表 - t_user&#xff08;三&#xff09;在用户表里插入3条记录 二&#xff0c;案例演示MyBatis基本使用&#xff08;一&#xff09;创建M…

JavaScript(JS)-1.JS基础知识

1.JavaScript概念 (1)JavaScript是一门跨平台&#xff0c;面向对象的脚本语言&#xff0c;来控制网页行为的&#xff0c;它能使网页可交互 (2)W3C标准&#xff1a;网页主要由三部分组成 ①结构&#xff1a;HTML负责网页的基本结构&#xff08;页面元素和内容&#xff09;。 …

[oeasy]python0136_接收输入_input函数_字符串_str

输入变量 回忆上次内容 上次研究了 一行赋值多个变量 a b 5a, b 7, 8 还研究了 标识符的惯用法 python使用的是 snake_case蛇形命名法用下划线 分隔开小写字母的 方法这样就可以 更合理地 命名变量了 变量变量 能变的量我可以 手工输入变量的值 吗&#xff1f;&#x1f9…

解决若依验证码异常:Error: image == null

前言 前两天在改项目突然发现若依的框架可以正常启动但是验证码加载不出来了&#xff0c;一直弹窗提示异常信息&#xff0c;下边是关于问题的描述和解决方案&#xff0c;没有耐心看过程的建议直接滑到最底下看解决方式 问题原因 登录页面一直提示 image null 如图 1 所示&…

基于数据驱动的智能空调系统需求响应可控潜力评估研究(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

网络协议-加密和HTTPs证书

目录 对称加密和非对称加密 加密解密 加密方法/解密方法 对称加密/非对称加密 非对称加密&#xff08;秘钥对&#xff09; 思考 解决信任问题 证书体系 算法如何验证证书就是Alibaba 实际的证书体系 ​编辑 常见算法介绍 DES&#xff08;Data Encryption Standard&…

【Go】一、Go语言基础

Go基础学习 Go基础1、开发环境2、Go语言的主要特征2.1、Go语言的数据类型2.2、Go语言基础语法2.3、Go语言变量2.4、Go语言常量 3、Go内置类型和函数4、init函数和main函数4.1、init函数4.2、main函数 5、命令6、运算符7、下划线7.1、下划线在import中7.2、下划线在代码中 8、数…

霍纳法则(Horner‘s rule)

文章目录 霍纳法则(Horners rule)程序设计程序分析霍纳法则(Horner’s rule) 【问题描述】 用霍纳法则求一个多项式在一个给定点的值 【输入形式】输入三行,第一行是一个整数n,表示的是多项式的最高次数;第二行多项式的系数组P[0…n](从低到高存储);第三行是一个整数…