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)的一个主要实现方式。能够实现代码更加灵活,更具有结构化。类作用都是提供一个模板,通过类可以创建多个具有相同结构的对象。 // 类的定义,与对象…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
【Ftrace 专栏】Ftrace 参考博文
ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...
Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用
Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础,但这一子系统结构复杂,常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题,需要一套工具化、…...
LeetCode 0386.字典序排数:细心总结条件
【LetMeFly】386.字典序排数:细心总结条件 力扣题目链接:https://leetcode.cn/problems/lexicographical-numbers/ 给你一个整数 n ,按字典序返回范围 [1, n] 内所有整数。 你必须设计一个时间复杂度为 O(n) 且使用 O(1) 额外空间的算法。…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
