ThreadLocal 内存泄漏原因和解决方法
一、ThreadLocal 内存泄漏的原因
ThreadLocal 的内存泄漏问题主要与其底层实现 ThreadLocalMap 的结构和垃圾回收机制有关。以下是核心原因:
1、ThreadLocalMap 的 Entry 设计
键(Key)是弱引用:ThreadLocalMap 中的键(Key)是 ThreadLocal 对象的弱引用(WeakReference<ThreadLocal<?>>)
值(Value)是强引用:Entry 中的值(Value)是强引用
2、弱引用的特性
当 ThreadLocal 对象,没有其他强引用指向它时(例如开发者将其置为 null),垃圾回收器(GC)会回收这个 ThreadLocal 对象
此时,ThreadLocalMap 中对应的 Entry 的 Key 会变为 null,但 Value 仍然是强引用,无法被 GC 回收
3、线程生命周期问题
如果线程是长时间运行的(例如线程池中的线程),并且 ThreadLocal 的 Value 未被清理:
Key 被回收后,Entry 的 Value 会一直存在,导致内存泄漏。
最终,ThreadLocalMap 中会积累大量 Key 为 null 的 Entry,占用内存
二、内存泄漏的示例场景
public class LeakExample {
private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
// 线程池中的线程会复用
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(() -> {
threadLocal.set(new byte[10 * 1024 * 1024]); // 设置大对象
// 没有调用 threadLocal.remove()
});
}
}
线程执行完毕后,ThreadLocal 的 Value(byte[10MB])会一直存在于 ThreadLocalMap 中,无法被回收
三、解决内存泄漏的方法
1、显式调用 remove()
关键操作:在使用完 ThreadLocal 后,调用 remove() 方法清理当前线程的 Entry。
最佳实践:使用 try-finally 确保清理:
2、将 ThreadLocal 声明为 static final
原因:如果 ThreadLocal 实例是非静态的,每次创建外部类实例时都会生成新的 ThreadLocal 对象,导致 ThreadLocalMap 中积累大量无用的 Entry
正确做法:
3、避免线程池中的线程长期持有 Value
线程池场景:线程会被复用,如果未清理 ThreadLocal,旧 Value 会一直存在。
解决方案:在任务执行前后调用 remove()。
四、JDK 对内存泄漏的优化
expungeStaleEntry() 方法:
在 ThreadLocalMap 的 get()/set()/remove() 方法中,JDK 会尝试清理 Key 为 null 的 Entry。
局限性:如果长时间不调用这些方法,泄漏仍会发生。
五、总结:如何避免内存泄漏
1、及时清理:在代码逻辑结束时调用 threadLocal.remove()。
2、使用 static final:减少不必要的 ThreadLocal 实例。
3、避免长生命周期线程:在池化线程场景中,显式清理 ThreadLocal。
4、工具检测:使用内存分析工具(如 MAT)检查 ThreadLocalMap 中的残留 Value。
六、关键理解
弱引用不是万能的:ThreadLocal 的弱引用仅解决了 Key 的内存泄漏,但 Value 仍需手动清理。
开发者责任:内存泄漏的根源是未正确管理 ThreadLocal 的生命周期,而非 JDK 设计缺陷。
ThreadLocal 内存泄漏原因和解决方法的更多相关文章
- 上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法
近来在负责公司短信网关的维护及建设,随着公司业务发展对短信依赖越来越严重了,短信每天发送量也比曾经每天40多w发送量暴增到每天达到200w发送量.由于是採用Java做发送底层,压力递增情况下不可避免的 ...
- Android 内存泄漏分析与解决方法
在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...
- c++内存泄漏原因及解决办法(智能指针)
内存泄漏 由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费. 内存泄露的 ...
- 使用HandyJSON导致的内存泄漏问题相关解决方法
在移动开发中,与服务器打交道是不可避免的,从服务器拿到的接口数据最终都会被我们解析成模型,现在比较常见的数据传输格式是json格式,对json格式的解析可以使用原生的解析方式,也可以使用第三方的,我们 ...
- MFC多线程内存泄漏问题&解决方法
在用visual studio进行界面编程时(如MFC),前台UI我们能够通过MFC的消息循环机制实现.而对于后台的数据处理.我们可能会用到多线程来处理. 那么对于大多数人(尤其是我这样的菜鸟),一个 ...
- jmeter(二十二)内存溢出原因及解决方法
jmeter是一个java开发的开源性能测试工具,在性能测试中可支持模拟并发压测,但有时候当模拟并发请求较大或者脚本运行时间较长时,压力机会出现卡顿甚至报异常————内存溢出, 这里就介绍下如何解决内 ...
- <转>jmeter(二十二)内存溢出原因及解决方法
本博客转载自:http://www.cnblogs.com/imyalost/category/846346.html 个人感觉不错,对jmeter讲解非常详细,担心以后找不到了,所以转发出来,留着慢 ...
- android 常见内存泄漏原因及解决办法
android常见内存泄漏主要有以下几类: 一.Handler 引起的内存泄漏. 在Android开发中,我们经常会使用Handler来控制主线程UI程序的界面变化,使用非常简单方便,但是稍不注意,很 ...
- jmeter 内存溢出原因及解决方法
jmeter是一个java开发的开源性能测试工具,在性能测试中可支持模拟并发压测,但有时候当模拟并发请求较大或者脚本运行时间较长时,压力机会出现卡顿甚至报异常————内存溢出, 这里就介绍下如何解决内 ...
- Windows 系统提示“内存不足”的原因及解决方法
Windows 系统提示“内存不足”的原因及解决方法 windows XP vista 及windows 7系统的电脑有时候会出现系统提示“内存不足”,这是由多方面原因造成的.本文具体分析下 ...
随机推荐
- C#遍历获取文件夹下所有文件
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using ...
- C 简答题
1.从C语⾔执⾏效率⽅便,简述下C语⾔采取了哪些措施提⾼执⾏效率.(14分 or 20分)(年年考,⾮常重要) ①使⽤指针:有些程序⽤其他语⾔也可以实现,但C能够更有效地实现:有些程序⽆法⽤其它语⾔实 ...
- Collection接口与其子接口实现类-----总复习
数组与集合 1. 集合与数组存储数据概述:集合.数组都是对多个数据进行存储操作的结构,简称Java容器.说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi, ...
- Git的一些基本用法
本文分享自天翼云开发者社区<Git的一些基本用法>,作者:l****n 基本操作 git branch 查看当前分支 git branch -a 查看所有分支 git pull 更新当前分 ...
- docker上安装并启动redis
//查看镜像 [root@VM-0-3-centos ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEredis latest cc69ae1 ...
- Linux 部署DVWA靶场
Linux 部署DVWA靶场 DVWA是一款开源的网络安全漏洞实践平台,专为安全学习者设计.它涵盖了XXS.SQL注入.文件上传.文件包含.CSRF和暴力破解等多种安全漏洞环境,每个漏洞都有从简单到复 ...
- Q:浏览器打开控制台报错:net::ERR_CONTENT_LENGTH_MISMATCH 206
一.问题描述 F12查看浏览器的控制台,提示net::ERR_CONTENT_LENGTH_MISMATCH 206 (Partial Content) ,如下图, HTTP状态码206表示" ...
- Mac启用或停用root用户
1.选取苹果菜单 () >"系统偏好设置",然后点按"用户与群组"(或"帐户"). 2.点按 ,然后输入管理员名称和密码. 3.点按 ...
- Luogu P11230 CSP-J 2024 接龙 题解 [ 线性 dp ] [ 前缀和 ]
接龙:一个前缀和优化 dp 或者单调队列优化 dp 的题目. 怎么周围的人都秒了 T3 不会 T4 啊,只有我觉得 T4 很套路,T3 比较难写吗. 暴力 dp 为了避免多维的状态定义,我们把每个人的 ...
- nacos(三): 创建第一个生产者producer(单体)
因为springcloud各个版本之间适配非常神经质,所以事件明确,在本实验环节中:使用的是JDK8,选择的springboot版本是2.7.6. 可以借助阿里云的脚手架(点此进入)帮我们创建第一个s ...