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

【并发】ThreadLocal 为什么会内存泄露

ThreadLocal 引起内存泄漏的原因主要与 ThreadLocalMap 的实现方式有关。ThreadLocalMap 使用了弱引用来存储 ThreadLocal 对象,但是它的值是强引用。如果不正确地使用 ThreadLocal 或者忘记在适当的时候移除 ThreadLocal 值,可能会导致内存泄漏。

内存泄漏的原因

  1. 弱引用和强引用的组合

    • ThreadLocalMap 使用 WeakReference<ThreadLocal<?>> 来存储 ThreadLocal 键,这意味着当 ThreadLocal 实例没有其他强引用时,GC 可以回收它。
    • 但是,ThreadLocalMap 中的 Entry 结构同时保存了对 ThreadLocal 值的强引用,即使 ThreadLocal 本身被回收,值对象仍然存在于 ThreadLocalMap 中。
  2. ThreadLocal 的生命周期

    • 如果 ThreadLocal 实例在代码中没有显式的强引用,并且没有手动调用 remove() 方法,ThreadLocal 可能会被 GC 回收,而 ThreadLocalMap 中的 Entry 会变成一个 keynull 的条目,导致 value 不能被回收,从而引发内存泄漏。

示例

考虑以下示例代码,如果不调用 remove() 方法,可能会导致内存泄漏:

public class ThreadLocalLeakExample {private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();public static void main(String[] args) {Thread thread = new Thread(() -> {// 分配一个大的数组,模拟消耗内存的对象threadLocal.set(new byte[1024 * 1024 * 10]); // 10MB// 这里不调用 threadLocal.remove() 或者 threadLocal.set(null)});thread.start();try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}// 线程结束,但如果不移除,10MB 的内存不会被回收}
}

避免内存泄漏的方法

  1. 显式调用 remove()

    • 在使用 ThreadLocal 后,显式调用 remove() 方法,以清除当前线程的 ThreadLocal 值。
    threadLocal.remove();
    
  2. 使用 try-finally 块

    • 确保在每次使用完 ThreadLocal 后,都调用 remove() 方法。
    try {threadLocal.set(new byte[1024 * 1024 * 10]); // 10MB// 使用 threadLocal 的值
    } finally {threadLocal.remove();
    }
    
  3. 线程池中的使用

    • 在使用线程池时,特别要注意 ThreadLocal 的内存泄漏问题。因为线程池中的线程会被重用,可能导致 ThreadLocal 的值一直保留在线程中。
    • 在每个任务完成后,显式调用 remove() 以防止内存泄漏。

总结

内存泄漏主要发生在以下情况下:

  • ThreadLocal 对象被 GC 回收,但是 ThreadLocalMap 中的 value 没有被清理。
  • 尤其在使用线程池时,需要特别注意及时清理 ThreadLocal 值,以避免长期占用内存。

通过正确的使用方式和及时清理,可以有效避免由于 ThreadLocal 引起的内存泄漏。

相关文章:

  • WAF,全称Web Application Firewall,好用WAF推荐
  • Ubuntu上安装Git:简单步骤指南
  • 字母象形:十分有趣的单词扩展逻辑
  • 联想电脑怎么开启vt_联想电脑开启vt虚拟化教程(附intel和amd主板开启方法)
  • 等保测评:企业数字安全的坚实盾牌
  • 【2024.9.29练习】R 格式
  • 在Spring项目中使用MD5对数据库加密
  • 【计算机网络】详解HTTP请求和响应格式常见请求方法Header报头响应报文状态码URL
  • C语言-进程控制编程
  • ceph rgw 桶分片之reshard
  • The Sandbox 游戏制作教程第 6 章|如何使用装备制作出色的游戏 —— 避免环境危险
  • 数据库 - python操作MySQL
  • 【C语言】tcp接收服务
  • 使用 Spring Boot 实现 JWT 生成与验证的自定义类
  • Library介绍(三)
  • 11111111
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Angular 4.x 动态创建组件
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • interface和setter,getter
  • jQuery(一)
  • miaov-React 最佳入门
  • node和express搭建代理服务器(源码)
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • Python - 闭包Closure
  • ubuntu 下nginx安装 并支持https协议
  • vagrant 添加本地 box 安装 laravel homestead
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 大主子表关联的性能优化方法
  • 服务器之间,相同帐号,实现免密钥登录
  • 解析 Webpack中import、require、按需加载的执行过程
  • 马上搞懂 GeoJSON
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • 如何正确理解,内页权重高于首页?
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • $.proxy和$.extend
  • $nextTick的使用场景介绍
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (MTK)java文件添加简单接口并配置相应的SELinux avc 权限笔记2
  • (pojstep1.3.1)1017(构造法模拟)
  • (web自动化测试+python)1
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (生成器)yield与(迭代器)generator
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)Linq学习笔记
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • .Net CF下精确的计时器
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .NET MAUI Sqlite数据库操作(二)异步初始化方法
  • .net mvc actionresult 返回字符串_.NET架构师知识普及