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

ThreadLocal 用了 WeakReference,为什么还会内存泄漏

先看 ThreadLocal 的存储结构ThreadLocal 本身不存数据数据存在每个Thread对象里的一个ThreadLocalMap字段上。ThreadLocalMap是ThreadLocal的内部类结构类似 HashMapkey 是ThreadLocal实例的弱引用value 是你放进去的对象。Thread └── ThreadLocalMap ├── Entry(WeakRefThreadLocal1, value1) ├── Entry(WeakRefThreadLocal2, value2) └── ...这里有个关键设计key 是弱引用value 是强引用。泄漏发生的条件泄漏需要同时满足两件事条件一ThreadLocal 实例本身不再被强引用。// 这个 ThreadLocal 定义为局部变量方法返回后就没有强引用了 public void doSomething() { ThreadLocalBigObject tl new ThreadLocal(); tl.set(new BigObject()); // ... 方法结束tl 这个局部变量消失ThreadLocal 实例只剩 ThreadLocalMap 里的弱引用 }static 字段持有的 ThreadLocal 不会出现这种情况因为 static 字段的强引用一直在。条件二这个线程没有结束还在被复用。这就是为什么泄漏主要发生在线程池里。线程池里的线程不会结束Thread对象一直存活它里面的ThreadLocalMap也一直存活。把两个条件合在一起看ThreadLocal 实例被 GC 了对应的 Entry 的 key 变成 null但 Entry 本身还挂在 ThreadLocalMap 里value 里的对象被 Entry 强引用着GC 回收不了。线程活多久这个对象就占多久内存。如果线程池有 200 个线程每次处理请求都往 ThreadLocal 里放了一个 200KB 的对象处理完没有removeThreadLocal 实例也被 GC 了那 ThreadLocalMap 里就会慢慢积累一批 key 为 null 的 Entry每个 Entry 的 value 都挂着 200KB 的数据不会被回收。为什么 WeakReference 没有解决这个问题弱引用只解决了 key 的问题ThreadLocal 实例本身可以被 GC 回收不会因为 ThreadLocalMap 里的引用而活着。但 value 还是强引用。key 被 GC 后value 就成了孤儿——没有任何途径从外部访问到它也没有任何东西会主动清理它只有等这个线程的ThreadLocalMap被整体回收即线程结束。ThreadLocal 自己做了一些防御get()、set()、remove()的时候会顺带清理 key 为 null 的 EntryexpungeStaleEntry。但这是被动清理且不是每次都触发依赖概率不能依赖它来防泄漏。正确的用法用完显式调用remove()放在finally块里确保执行private static final ThreadLocalRequestContext CONTEXT new ThreadLocal(); public void handleRequest(Request request) { try { CONTEXT.set(new RequestContext(request)); doWork(); } finally { CONTEXT.remove(); // 必须在 finally 里保证异常时也执行 } }Web 框架里常见的 MDCMapped Diagnostic Context也要记得清理// 在 Filter 或 Interceptor 里 try { MDC.put(requestId, UUID.randomUUID().toString()); MDC.put(userId, String.valueOf(userId)); chain.doFilter(request, response); } finally { MDC.clear(); // 底层也是 ThreadLocal }MDC 很容易被忽略。很多团队用了 MDC 打 traceId但没有在请求处理完之后 clear线程池里的线程带着上一个请求的 MDC 数据处理下一个请求日志里的 traceId 对不上但很难察觉。常见的踩坑场景用 Spring 的Async方法时调用方线程里的 ThreadLocal 数据不会自动传递到新线程。需要显式传递Async public void asyncTask() { // 这里拿不到调用方线程里的 ThreadLocal 数据 // 如果需要要在提交任务时手动把数据传过来 }如果业务上确实需要跨线程传递上下文比如 traceId 要传到异步线程用InheritableThreadLocal——子线程创建时会从父线程复制一份数据。但在线程池场景里线程不是每次都新建InheritableThreadLocal 的复制逻辑不会触发仍然需要手动处理。Alibaba 开源的transmittable-thread-localTTL解决了这个问题在Runnable/Callable被提交到线程池时捕获当前线程的上下文在任务执行时恢复执行完清理。如果系统里有大量跨线程传递上下文的需求TTL 是更可靠的方案。
http://www.zskr.cn/news/1395273.html

相关文章:

  • lilac_paper_deep_dive_markdown_cn
  • 外卖系统小程序开发方案解析:直播、团购与外卖功能如何融合
  • 招聘律师与所业务范围
  • 免费CRM系统有哪些?一文分清真假免费,中小企业零成本选型攻略
  • 为内容创作平台集成Taotoken实现多模型文章辅助生成与润色
  • 独立开发者如何借助Taotoken构建多模型AI助手应用
  • GNN与强化学习融合:构建动态个性化推荐系统的核心技术解析
  • t-SNE非线性降维结合深度学习提升高光谱图像分类精度
  • 2026年5月北京钻石回收店推荐:正规靠谱商家指南与收的顶实测 - 奢侈品回收测评
  • H.264压缩域低码率鲁棒水印:原理、实现与工程实践
  • 用过才敢说!盘点2026年人气爆表的的AI论文工具
  • TAE-GAN:融合情感分析的文本到图像生成模型实践
  • 实测才敢推!2026年最值得信赖的专业降AI率网站
  • iPhone17首次降价就杀疯了!销量直逼3000万台
  • 利用taotoken为ubuntu上的claude code提供稳定可靠的api后备支持
  • Wireshark蓝牙抓包
  • 桌面全能N合一工具 一个软件搞定时钟 日历 天气 记事 屏保 任务栏全场景
  • 动态优化集成学习:解决中医文本命名实体识别中的类别不平衡与实体稀疏问题
  • 机器学习赋能硬件安全:从漏洞检测到侧信道分析的实践指南
  • Redis 有序集合(Zset / Sorted Set)
  • 1.4t4
  • 从理想模型到现实调度:WFQ算法的公平性保障与实现挑战
  • VMware9.1升级CSR报错修复+vSphere容器与Docker区别详解
  • 2026最危险的AI工具排名——不是垫底的,而是排第4、第6、第9的“高分伪强者”,它们正悄悄拖垮你的交付周期
  • 2026年PMP最佳报考时间已定!过来人建议:优先锁定9月
  • 基于图神经网络与社区检测的教育公平性分析:从数据洞察到精准干预
  • 2026 实测明星同款声音克隆 AI 工具 短视频批量创作合规高还原优选榜单 - 品牌企业推荐师(官方)
  • LoRA微调技术:破解低资源语言机器翻译难题的实践指南
  • 国内AI生图工具性价比之王:100就能get可编辑矢量图
  • 婚恋交友系统源码|线上智能匹配+线下活动联动+实名认证安全社交解决方案