ThreadLocal内存泄漏分析
一、ThreadLocal内存泄漏分析
1.1 ThreadLocal实现原理
1.1.1、set(T value)方法
查看ThreadLocal源码的 set(T value)方法,可以发现数据是存在了ThreadLocalMap的静态内部类Entry里面
其中key为使用弱引用的ThreadLocal实例,value为set传入的值。核心源代码:
public void set(T value) {Thread t = Thread.currentThread();// ThreadLocalMap跟当前线程对象绑定,是线程对象中的一个成员属性ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);else// 第一次调用的时候,将当前线程和value往下传。createMap(t, value);
}
void createMap(Thread t, T firstValue) {// 当前线程内部的变量 ThreadLocal.ThreadLocalMap threadLocals 设置为新建出来的对象。t.threadLocals = new ThreadLocalMap(this, firstValue);
}
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;
Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}...
}
1.2 ThreadLocal 内存泄漏的原因
引用原理图如下,实心箭头表示强引用,虚线箭头表示弱引用。
1、图中所示,当前线程强引用了ThreadLocalMap,而ThreadLocal为ThreadLocalMap的弱引用Key。
2、结合1.2节知识背景,如果ThreadLocal没有被外部强引用,当系统触发GC时,会将ThreadLocal对象回收掉,会导致ThreadLocalMap的Key为null,但是value还是被当前线程强引用,只有当Thread线程退出后,value的强引用链才会断开。
3、如果线程不结束(比如使用了线程池),则引用链(Thread -> ThreadLocalMap -> Entry -> value)一直存在,永远不会被回收,从而造成内存泄漏。
1.2.1 代码演示内存泄漏
public class ThreadLocalTest {public static void main(String[] args) {//二、TheadLocal内存泄漏//2.1、局部代码块中创建ThreadLocal后不引用它。createThreadLocal();//2.2、让GC回收不再被强引用,只有弱引用的TheadLocal对象System.gc();//2.3、查看线程成员属性ThreadLocalMap中 存入的键值对(key为null,而value还在,出现内存泄漏问题)Thread thread = Thread.currentThread();}
public static ThreadLocal<String> createThreadLocal(){ThreadLocal<String> threadLocal = new ThreadLocal<>();threadLocal.set("zs");return threadLocal;}
}
debug运行代码,查看当前线程的ThreadLocalMap中的数据,可以发现引用的Key已经被GC回收了,造成了内存泄漏
1.3 key为什么使用弱引用
即使没有手动删除key和value,ThreadLocal在没有被引用的时候也会被回收。即ThreadLocalMap的key为null,下一次ThreadLocalMap调用set()、get()、remove()方法的时候会清除没被回收的value。
1.3.1 代码演示清除没被回收的value
package com.adolesce.server.mutithread;
/*** @author Administrator* @version 1.0* @description: TODO* @date 2023/7/1 9:52*/
public class ThreadLocalTest {public static void main(String[] args) {//三、TheadLocal没被强引用后,触发System.gc(),将key回收,设为null//3.1、创建ThreadLocal对象ThreadLocal threadLocal = createThreadLocal();//3.2、模拟threadLocal没被引用:断点时手动将thread中ThreadLocalMap value为【zs】对应的key设为nullThread thread = Thread.currentThread();//3.3、测试get、set、remove方法将key为null的value清除threadLocal.get();//3.4、查看ThreadLocalMap中value是否为null了thread = Thread.currentThread();}
public static ThreadLocal<String> createThreadLocal(){ThreadLocal<String> threadLocal = new ThreadLocal<>();threadLocal.set("zs");return threadLocal;}
}

调用get方法的时候,由于map中的value对应的key为null,通过当前ThreadLocal对象去获取是获取value是获取不到Entry,于是调用初始化value的方法,清除原来的value,如get源码:
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) { // 为null跳出判断@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue(); // 调用初始化value的方法,清除原来的value
}
二、ThreadLocal的正确使用方法
1、每次使用完ThreadLocal都调用它的remove()方法清除数据。
2、将ThreadLocal变量定义成 private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能
通过 ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
package com.adolesce.server.mutithread;
/*** @author Administrator* @version 1.0* @description: TODO* @date 2023/7/1 9:52*/
public class ThreadLocalTest {private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {//一、TheadLocal使用System.out.println("主线程开启,线程ID:" + Thread.currentThread().getId());//1.1、向ThreadLocalMap中存入键值对setData();//1.2、从ThreadLocal中获取数据getData();//1.3、清除(通常在拦截器的afterCompletion()方法中进行清除)removeData();}
private static void setData() {threadLocal.set("zhangsan");System.out.println("在线程"+Thread.currentThread().getId()+"中向ThreadLocal中存入了姓名");}
private static void getData() {String name = threadLocal.get();System.out.println("在线程"+Thread.currentThread().getId()+"中从ThreadLocal中取了姓名:" + name);}private static void removeData() {threadLocal.remove();}
}
三、总结
1、内存泄漏原因:我们使用ThreadLocal过程中,如果ThreadLocal对象强引用断掉后,只剩弱引用,ThreadLocal对象会被回收,此时ThreadLocal中的key会变为null,而value没有被回收,同时又由于ThreadLocalMap是Thread中的成员属性,与Thread对象的生命周期是一样长,如果当前线程一直未被销毁,又没有手动删除对应key,这样就会导致value内存泄漏。
2、使用弱引用可以多一层value回收的保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set()、get()、remove()的时候会被清除。
3、因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
相关文章:
ThreadLocal内存泄漏分析
一、ThreadLocal内存泄漏分析 1.1 ThreadLocal实现原理 1.1.1、set(T value)方法 查看ThreadLocal源码的 set(T value)方法,可以发现数据是存在了ThreadLocalMap的静态内部类Entry里面 其中key为使用弱引用的ThreadLocal实例,value为set传入的值。核…...
第 30 章 XML
第 30 章 XML 1.IE 中的 XML 2.DOM2 中的 XML 3.跨浏览器处理 XML 随着互联网的发展,Web 应用程序的丰富,开发人员越来越希望能够使用客户端来操作 XML 技术。而 XML 技术一度成为存储和传输结构化数据的标准。所以,本章就详细探讨一下 Ja…...
VMware下的ubuntu显示文字太小的自适应显示调整
我的情况 我使用的是4K的32寸显示器,分辨率为 3840 x 2160,ubuntu版本为18.04,默认的情况下系统分辨率为 3466 x 1842。 此时,显示的文字很小,虽然可以看清,但也比较吃力,在VMware窗口…...
外贸网站怎么搭建对谷歌seo比较好?
外贸网站怎么搭建对谷歌seo比较好?搭建一个网站自然不复杂,但要想搭建一个符合谷歌seo规范的网站,那就要多注意了,你的网站做的再酷炫,再花里胡哨,但如果页面都是js代码,或者页面没有源代码内容…...
如何创建网络白名单
网络白名单(Whitelist)是指允许通过网络访问的特定设备、IP地址、应用程序或网站。与黑名单(Blacklist)相反,白名单机制默认阻止所有连接,只有在白名单中明确允许的访问才能通过。这种策略可以提高网络的安…...
前端动态创建svg不起效果?
document.createElement(path);诸如此类的创建一般都是不太行的 我在创建这个之后,虽然在网页上是有相应的结构,但是完全不显示 一般正确的创建方式为 document.createElementNS(http://www.w3.org/2000/svg,path);在使用document.createElementNS(“ht…...
三、Drf request对象
3.1django和drf中的request的区别 django中的request:用户请求对象和参数 drf中的request:将django中的request加了一层封装,又加了一些其它的参数 drf中的request._requestdjango中的request 3.2创建url路由和CBV class UserView(APIView):def get(self,requ…...
CMIS5.2_光模块切应用(Application Selection and Instantiation)
目录 重要概念 DP配置、应用声明、应用码的区别 Control Set Provision 和 Commission ApplyDPInit 和 ApplyImmediate 判断应用是否切换成功 以800G光模块的3个应用对应的DP配置举例 1*800G应用: 2*400G应用: 8*100G应用: 应用声明…...
网络安全 DVWA通关指南 DVWA Weak Session IDs(弱会话)
DVWA Weak Session IDs(弱会话) 文章目录 DVWA Weak Session IDs(弱会话)Low LevelMedium LevelHigh LevelImpossible Level 参考文献 WEB 安全靶场通关指南 相关阅读 Brute Force (爆破) Command Injection(命令注入…...
828华为云征文|华为云 Flexus X 实例初体验
一直想有自己的一款的服务器,为了更好的进行家庭娱乐,甚至偶尔可以满足个人搭建开发环境的需求,直到接触到了华为云 Flexus X 云服务器。Flexus 云服务器 X 实例是面向中小企业和开发者打造的轻量级云服务器。提供快速应用部署和简易的管理能…...
欧科云链OKLink相约TOKEN2049:更全面、多元与安全
过去几日,OKLink 与全球 Web3 从业者与爱好者们相约狮城。在多场激动人心的活动上分享了我们的产品进展、有关于链上数据的专家观点以及打磨产品的经验。同时也听到了很多来自行业的宝贵声音。跟随我们的脚步,捕捉这充实一周的精彩瞬间: 1、…...
遥感影像-语义分割数据集:云数据集详细介绍及训练样本处理流程
原始数据集详情 简介:该云数据集包括150张RGB三通道的高分辨率图像,在全球不同区域的分辨率从0.5米到15米不等。这些图像采集自谷歌Earth的五种主要土地覆盖类型,即水、植被、湿地、城市、冰雪和贫瘠土地。 KeyValue卫星类型谷歌Earth覆盖区…...
【有啥问啥】SimAM(Similarity-Aware Activation Module)注意力机制详解
SimAM(Similarity-Aware Activation Module)注意力机制详解 引言 在计算机视觉领域,注意力机制通过引导模型关注图像中的关键区域,显著提升了模型处理和理解图像的能力。SimAM(Similarity-Aware Activation Module&a…...
鸿蒙应用开发,如何保存登录信息
在鸿蒙应用开发中,保存登录信息是实现用户自动登录、个性化展示等功能的基础。以下是一些常用的保存登录信息的方法: 一、全局状态管理 对于简单的应用,可以在全局范围内定义一个类(如UserManager),使用单…...
★ C++进阶篇 ★ map和set
Ciallo~(∠・ω< )⌒☆ ~ 今天,我将继续和大家一起学习C进阶篇第四章----map和set ~ ❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️ 澄岚主页:椎名澄嵐-CSDN博客 C基础篇专栏:★ C基础篇 ★_椎名澄嵐的博客-CSDN博…...
Python知识点:如何使用Nvidia Jetson与Python进行边缘计算
开篇,先说一个好消息,截止到2025年1月1日前,翻到文末找到我,赠送定制版的开题报告和任务书,先到先得!过期不候! 如何使用Nvidia Jetson与Python进行边缘计算 Nvidia Jetson平台是专为边缘计算设…...
动态分配内存
目录 前言 一.malloc,free函数 1.malloc,free函数原型 2.使用方法 3.具体实例 4.注意事项 二.calloc函数 1.calloc函数原型 2.主要特点 3.使用案例 三.realloc函数 1.realloc函数原型 2.使用案例 3.注意事项 前言 动态内存是指在程序运行时,按需分配和…...
Unity Input System自动生成配置
参考视频 创建及配置新输入系统 New Input System|Unity2022.2 最新教程《勇士传说》入门到进阶|4K_哔哩哔哩_bilibili ProjectSettings设置 Unity编辑器菜单栏选择Edit->Project Settings->Player->Other Settings,将Api Compatibility Level…...
【Windows】在任务管理器中隐藏进程
在此前的一篇,我们已经介绍过了注入Dll 阻止任务管理器结束进程 -- Win 10/11。本篇利用 hook NtQuerySystemInformation 并进行断链的方法实现进程隐身,实测支持 taskmgr.exe 的任意多进程隐身。 任务管理器 代码: // dllmain.cpp : 定义 …...
【TypeScript学习】TypeScript基础学习总结二
主要记录ts中的类、接口与泛型 1.类 无论是在哪种语言中,类都是面向对象编程(OOP)的一个主要实现方式。能够实现代码更加灵活,更具有结构化。类作用都是提供一个模板,通过类可以创建多个具有相同结构的对象。 // 类的定义,与对象…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
