当前位置: 首页 > news >正文

图文详解ThreadLocal:原理、结构与内存泄漏解析


目录

一.什么是ThreadLocal

二.ThreadLocal的内部结构

三.ThreadLocal带来的内存泄露问题

▐ key强引用

▐ key弱引用

总结


一.什么是ThreadLocal

在Java中,ThreadLocal 类提供了一种方式,使得每个线程可以独立地持有自己的变量副本,而不是共享变量。这可以避免线程间的同步问题,因为每个线程只能访问自己的ThreadLocal变量。通过ThreadLocal为线程添加的值只能由这个线程访问到,其他的线程无法访问,因此就避免了多线程之间的同步问题

使用ThreadLocal时,通常需要实现以下步骤:

  • 初始化:创建ThreadLocal变量。
    private static ThreadLocal<T> threadLocal = new ThreadLocal<>();
    
  • 设置值:使用set(T value)方法为当前线程设置值。
    threadLocal.set(value);
    
  • 获取值:使用get()方法获取当前线程的值。
    T value = threadLocal.get();
    
  • 移除值:使用remove()方法在线程结束时清除ThreadLocal变量,以避免内存泄漏。
    threadLocal.remove();
    

在下面这个示例中,在主线程中存储了一个整形的10,新建一个线程后去取这个值是取不到的,因为该值只属于主线程,故输出为null

public class ThreadLocalExample {private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 设置线程局部变量的值threadLocal.set(10);// 这个值在其他线程中是取不到获取的new Thread(() -> {Integer value = threadLocal.get();//nullSystem.out.println("Thread value: " + value);}).start();}
}

二.ThreadLocal的内部结构

在JDK8之后每一个线程都会维护一个ThreadLoaclMap,这个Map是一个哈希散列结构,如下图所示,每一个元素(Entry)都是一个键值对,key为ThreadLocal,Value为存储的数据,也就是set()方法存储的内容。

但是在早期并不是这样的,早期的JDK中都是由ThreadLocal来维护这样的一个Map,里面的key则是Thread,就像下图这样

Thread线程数一般往往是大于ThreadLocal的,那么当线程销毁的时候对比俩个方案,JDK8的方案则可以节省更多的内存空间(只需要将对应的ThreadLocalMap删除),JDK8之前的方案由于Thread只是Map的一个节点的key,将其释放掉就会导致这块Map的空间利用率很低。

我们也可以打开ThreadLocalMap的核心源码,会发现正是JDK8方案所示的结构

以下是添加了中文注释的版本

static class ThreadLocalMap {/*** 存储的每个元素--Entry*/static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}/*** 初始容量--必须是2的整数幂*/private static final int INITIAL_CAPACITY = 16;/*** 存放数据的Table,长度也必须是2的整数幂*/private Entry[] table;/*** 数组内已使用的长度,即Entrys的个数*/private int size = 0;/*** 进行扩容的阈值*/private int threshold; // Default to 0
}

三.ThreadLocal带来的内存泄露问题

首先是内存泄漏的概念:

  • 内存溢出:没有足够的内存供申请者使用
  • 内存泄漏:程序中已经动态分配的内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至崩溃。此外内存泄漏的堆积最终也会导致内存溢出。

下图是ThreadLocal相关的内存结构图,在栈区中有threadLocal对象和当前线程对象,分别指向堆区真正存储的类对象,这俩个指向都是强引用。在堆区中当前线程肯定是只有自己的Map的信息的,而Map中又存储着一个个的Entry节点;在Entry节点中每一个Key都是ThreadLocal的实例,同时Value又指向了真正的存储的数据位置,以上便是下图的引用关系。

那么所谓的内存泄漏,其实就是指的Entry这块内存不能正确释放

有人可能会猜测出现内存泄漏是因为Entry中使用了弱引用的key(如下所示继承关系中的WeakReference),但这种理解其实是不对的

    static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}

强弱引用的概念:

  • 强引用(StrongReference):就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾回收器就不会回收这种对象。
  • 弱引用(WeakReference):垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 

▐ key强引用

我们可以按照强弱引用来分别推算一下,首先是强引用的情况

当我们在业务代码中使用完ThreadLocal,在栈区指向堆区的这个指向关系就会被回收掉了,但是由于Key是强引用指向ThreadLocal,故而堆区中的ThreadLocal无法被回收,此时的Key指向ThreadLocal,另外由于当前线程还没有结束,则下面那条强引用指向关系任然存在。故为下图的关系状态

在这样的情况下,由于栈上的指向已经消失了,我们无法访问到堆上的ThreadLocal,故而无法访问到Entry,但是Entry又有Map指向它,故而无法进行回收。那么此时的Entry即无法访问也无法回收,这就造成了Entry的内存溢出。

▐ key弱引用

其次是弱引用的情况,当我们在业务代码中使用完ThreadLocal就通过垃圾回收(GC)进行了回收,那么由于Key是弱引用,Key此时就指向null,但是由于当前线程还没有结束,则下面那条强引用指向关系任然存在

在这样的情况下,Entry由于仍然有Map指向它所以不会被GC回收掉,但是此时的Key又为null,所以我们无法访问到这个Value。这就导致了这个Value我们即不能访问到也不能进行回收,此时就造成了Value的内存泄漏。

总结

通过以上分析,我们得知了不管Entry中的Key是否为弱引用,都会造成内存泄漏的情况,只不过强引用下是Entry的内存泄漏,弱引用下是Value的内存泄漏。造成这样内存泄漏的情况都有这样的共同特性:

  • 都没有手动删除Entry
  • 当先线程都在运行

也就是说,只要我们在使用完ThreadLocal后,调用其remove()方法删除对应的Entry就可以避免内存泄漏的问题。

并且由于ThreadLoaclMap是Thread的一个属性,故而它的生命周期和线程一样,那么当线程的生命周期结束,自然也就没有Map指向Entry,这也就在根源上解决了问题。

综上所述,造成ThreadLoacl内存泄漏的根本原因是:由于ThreadLoaclMap的生命周期和Thread一样长,如果没有手动删除对应的Key就会导致内存泄漏。




 本次的分享就到此为止了,希望我的分享能给您带来帮助,创作不易也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

相关文章:

图文详解ThreadLocal:原理、结构与内存泄漏解析

目录 一.什么是ThreadLocal 二.ThreadLocal的内部结构 三.ThreadLocal带来的内存泄露问题 ▐ key强引用 ▐ key弱引用 总结 一.什么是ThreadLocal 在Java中&#xff0c;ThreadLocal 类提供了一种方式&#xff0c;使得每个线程可以独立地持有自己的变量副本&#xff0c;而…...

基于java的综合小区管理系统论文.doc

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统综合小区管理系统信息管理难度大&#xff0c;容错率低&am…...

如何合理设置PostgreSQL的`max_connections`参数

合理设置PostgreSQL的max_connections参数对于数据库的稳定性和性能至关重要。这个设置值决定了允许同时连接到数据库的最大客户端数量。如果设置不当&#xff0c;可能导致资源浪费或系统过载。以下是设置max_connections时需要考虑的几个关键因素&#xff1a; 1. 评估系统硬件…...

Kubectl 常用命令汇总大全

kubectl 是 Kubernetes 自带的客户端&#xff0c;可以用它来直接操作 Kubernetes 集群。 从用户角度来说&#xff0c;kubectl 就是控制 Kubernetes 的驾驶舱&#xff0c;它允许你执行所有可能的 Kubernetes 操作&#xff1b;从技术角度来看&#xff0c;kubectl 就是 Kubernetes…...

【Linux】Linux环境基础开发工具使用之Linux调试器-gdb使用

目录 一、程序发布模式1.1 debug模式1.2 release模式 二、默认发布模式三、gdb的使用结尾 一、程序发布模式 程序的发布方式有两种&#xff0c;debug模式和release模式 1.1 debug模式 目的&#xff1a;主要用于开发和测试阶段&#xff0c;目的是让开发者能够更容易地调试和跟…...

clickhouse_driver

一、简介 clickhouse_driver是一个Python库&#xff0c;用于与ClickHouse数据库进行交互。ClickHouse是一个高性能的列式数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;它适用于实时分析&#xff08;OLAP&#xff09;场景。clickhouse_driver模块提供了与ClickHouse…...

BI分析实操案例分享:零售企业如何利用BI工具对销售数据进行分析?

在当下这个竞争激烈的零售市场&#xff0c;企业如何在波诡云谲的商场中站稳脚跟&#xff0c;实现销售目标的翻倍增长&#xff1f; 答案可能就藏在那些看似杂乱无章的数字里。 是的&#xff0c;你没有看错&#xff0c;答案正是那些我们日常接触的销售数据。它们就像是宝藏&…...

python : Requests请求库入门使用指南 + 简单爬取豆瓣影评

Requests 是一个用于发送 HTTP 请求的简单易用的 Python 库。它能够处理多种 HTTP 请求方法&#xff0c;如 GET、POST、PUT、DELETE 等&#xff0c;并简化了 HTTP 请求流程。对于想要进行网络爬虫或 API 调用的开发者来说&#xff0c;Requests 是一个非常有用的工具。在今天的博…...

宋红康JVM调优思维导图

文章目录 1. 概述2. JVM监控及诊断命令-命令行篇3. JVM监控及诊断工具-GUI篇4. JVM运行时参数5. 分析GC日志 课程地址 1. 概述 2. JVM监控及诊断命令-命令行篇 3. JVM监控及诊断工具-GUI篇 4. JVM运行时参数 5. 分析GC日志...

linux 网卡配置

linux网卡可以通过命令和配置文件配置,如果是桌面环境还可以通过图形化界面配置. 1.ifconfig(interfaces config)命令方式 通常需要以root身份登录或使用sudo以便在Linux机器上使用ifconfig工具。依赖于ifconfig命令中使用一些选项属性&#xff0c;ifconfig工具不仅可以被用来…...

IEEE |第五届机器学习与计算机应用国际学术会议(ICMLCA 2024)

第五届机器学习与计算机应用国际学术会议(ICMLCA 2024)定于2024年10月18-20日在中国杭州隆重举行。本届会议将主要关注机器学习和计算机应用面临的新的挑战问题和研究方向&#xff0c;着力反映国际机器学习和计算机应用相关技术研究的最新进展。 IEEE |第五届机器学习与计算机应…...

【网络安全】漏洞挖掘:IDOR实例

未经许可&#xff0c;不得转载。 文章目录 正文 正文 某提交系统&#xff0c;可以选择打印或下载passport。 点击Documents > Download后&#xff0c;应用程序将执行 HTTP GET 请求&#xff1a; /production/api/v1/attachment?id4550381&enamemId123888id为文件id&am…...

vue项目执行 cnpm install 报错证书过期的解决方案

拉下源码后执行依赖安装过程&#xff0c;报错 error Error: Certificate has expired&#xff0c;可以通过一下方发解决&#xff1a;npm config set strict-ssl false 再执行 cnpm 命令即可正常拉依赖...

XGboost的安装与使用

安装xgboost&#xff1a; conda install py-xgboost下载demo的数据&#xff1a; https://github.com/dmlc/xgboost 安装graphviz conda install python-graphviz数据 在demo/data里面&#xff1a; 训练集是&#xff1a;agaricus.txt.train、测试集是&#xff1a;agaricus…...

【AI趋势9】开源普惠

关于开源的问题&#xff0c;可以参考我之前的文章&#xff1a; 再说开源软件-CSDN博客 【AI】马斯克说大模型要开源&#xff0c;我们缺的是源代码&#xff1f;&#xff08;附一图看懂6大开源协议&#xff09;_分开源和闭源,我们要的当然是开源,马斯克开源。-CSDN博客 一、开…...

【Spark集群部署系列一】Spark local模式介绍和搭建以及使用(内含Linux安装Anaconda)

简介 注意&#xff1a; 在部署spark集群前&#xff0c;请部署好Hadoop集群&#xff0c;jdk8【当然Hadoop集群需要运行在jdk上】&#xff0c;需要注意hadoop&#xff0c;spark的版本&#xff0c;考虑兼容问题。比如hadoop3.0以上的才兼容spark3.0以上的。 下面是Hadoop集群部署…...

泛微OA 常用数据库表

HrmDepartment 人力资源部门 HrmSubCompany 人力资源分部 HrmResource 员工信息表 HrmRoles 角色信息表 T_Condition 报表条件 T_ConditionDetail 报表条件详细值 T_DatacenterUser 基层用户信息 T_FadeBespeak 调查退订表 T_fieldItem 调查项目表输入项信息 T_fieldItemDetail…...

宜佰丰超市进销存管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; JavaMysql 工具&#xff1a; IDEA/Eclipse、Navicat、Maven 系统展示 首页 管理员功能模块…...

生成Vue脚手架报错:npm error code ETIMEDOUT

遇到 ETIMEDOUT 错误通常表示你的 npm 请求在尝试连接到 npm 仓库&#xff08;如 https://registry.npmjs.org&#xff09;时超时了。这个问题通常与网络连接、代理设置或网络配置有关。以下是一些解决这个问题的步骤&#xff1a; 检查网络连接&#xff1a; 确保你的设备可以正…...

Readiness Probe可以解决应用启动慢造成访问异常的问题。

Readiness Probe可以解决应用启动慢造成访问异常的问题。 正确 错误 这句话是正确的。 ‌Readiness Probe确实可以解决应用启动慢造成的访问异常问题。‌ Readiness Probe&#xff0c;也称为就绪性探针&#xff0c;是Kubernetes中用于监控容器应用状态稳定性的重要机制之一。…...

DriverStore Explorer:突破Windows驱动管理瓶颈,释放系统空间提升80%存储效率

DriverStore Explorer&#xff1a;突破Windows驱动管理瓶颈&#xff0c;释放系统空间提升80%存储效率 【免费下载链接】DriverStoreExplorer Driver Store Explorer [RAPR] 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 诊断存储异常&#xff1a;设…...

Gemma-3-12B-IT WebUI惊艳效果:Agent框架设计+Tool Calling实现

Gemma-3-12B-IT WebUI惊艳效果&#xff1a;Agent框架设计Tool Calling实现 1. 引言&#xff1a;当大模型拥有“手”和“眼” 想象一下&#xff0c;你正在和一个非常聪明的助手聊天。它能回答你的问题&#xff0c;帮你写代码&#xff0c;甚至能创作故事。但当你问它“现在几点…...

Vivado 2020.2实战:XDMA IP核配置全解析(含PCIe 2.0速率计算避坑指南)

Vivado 2020.2实战&#xff1a;XDMA IP核配置全解析&#xff08;含PCIe 2.0速率计算避坑指南&#xff09; 在FPGA与主机间的高速数据交互场景中&#xff0c;PCIe协议凭借其高带宽和低延迟特性成为首选方案。Xilinx提供的XDMA IP核作为PCIe与AXI总线的桥梁&#xff0c;其配置过程…...

从GCC-PHAT到深度学习:一种融合特征与神经网络的声源定位实践

1. 声源定位技术的前世今生 第一次接触声源定位是在2016年的一个智能音箱项目上&#xff0c;当时团队需要实现"唤醒词定向响应"功能。我们尝试了各种传统算法&#xff0c;最终在GCC-PHAT和SRP-PHAT之间反复调试的场景至今记忆犹新。这种让机器"听声辨位"的…...

TCC性能瓶颈到底卡在哪?:用Arthas+Metrics精准定位4大隐性耗时源并实测压降67%

第一章&#xff1a;TCC性能瓶颈到底卡在哪&#xff1f; TCC&#xff08;Try-Confirm-Cancel&#xff09;模式虽能保障分布式事务的强一致性&#xff0c;但其性能损耗远高于本地事务——根本原因并非网络延迟本身&#xff0c;而是其固有的三阶段协同机制与资源生命周期管理带来的…...

云计算算力价格波动:行业重构与竞争新格局

云计算价格反转&#xff1a;从价格战到集体涨价2025年4月&#xff0c;阿里云率先发起价格战&#xff0c;京东云、腾讯云、华为云等纷纷跟进&#xff0c;“最高降幅达60%”的口号让行业陷入价格混战。然而&#xff0c;到了2026年3月&#xff0c;市场风向突变&#xff0c;谷歌云、…...

理视康新零售系统开发要点

业务模式设计新零售模式需整合线上线下渠道&#xff0c;构建会员体系、分销机制与数据中台。通过小程序、APP或H5实现线上商城&#xff0c;线下门店采用智能硬件&#xff08;如AR试戴、智能货架&#xff09;提升体验。结合LBS技术实现附近门店导流&#xff0c;支持到店自提或同…...

实战演练:基于快马平台,快速搭建一个软件密钥授权管理后台原型

实战演练&#xff1a;基于快马平台&#xff0c;快速搭建一个软件密钥授权管理后台原型 最近在开发一个软件授权管理系统时&#xff0c;发现很多项目都需要类似的密钥管理功能。正好用InsCode(快马)平台快速搭建了一个原型&#xff0c;以VMware16密钥管理为例&#xff0c;分享一…...

P3916 图的遍历 题解(反向建图)

更好的阅读体验&#xff08;博客园&#xff09; 题面 P3916 图的遍历 题目描述 给出 NNN 个点&#xff0c;MMM 条边的有向图&#xff0c;对于每个点 vvv&#xff0c;令 A(v)A(v)A(v) 表示从点 vvv 出发&#xff0c;能到达的编号最大的点。现在请求出 A(1),A(2),…,A(N)A(1),…...

桌面图标杂乱如何高效管理?NoFences开源工具让文件归类效率提升60%

桌面图标杂乱如何高效管理&#xff1f;NoFences开源工具让文件归类效率提升60% 【免费下载链接】NoFences &#x1f6a7; Open Source Stardock Fences alternative 项目地址: https://gitcode.com/gh_mirrors/no/NoFences 每天面对布满数十个图标的电脑桌面&#xff0c…...