iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >为什么每次用完ThreadLocal都要调用remove()
  • 947
分享到

为什么每次用完ThreadLocal都要调用remove()

2024-04-02 19:04:59 947人浏览 安东尼
摘要

这篇文章主要介绍“为什么每次用完ThreadLocal都要调用remove()”,在日常操作中,相信很多人在为什么每次用完ThreadLocal都要调用remove()问题上存在疑惑,小编查阅了各式资料,整

这篇文章主要介绍“为什么每次用完ThreadLocal都要调用remove()”,在日常操作中,相信很多人在为什么每次用完ThreadLocal都要调用remove()问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”为什么每次用完ThreadLocal都要调用remove()”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

什么是内存泄漏

内存泄漏指的是,当某一个对象不再有用的时候,占用的内存却不能被回收,这就叫作内存泄漏。

因为通常情况下,如果一个对象不再有用,那么我们的垃圾回收器  GC,就应该把这部分内存给清理掉。这样的话,就可以让这部分内存后续重新分配到其他的地方去使用;否则,如果对象没有用,但一直不能被回收,这样的垃圾对象如果积累的越来越多,则会导致我们可用的内存越来越少,最后发生内存不够用的  OOM 错误。

下面我们来分析一下,在 ThreadLocal 中这样的内存泄漏是如何发生的。

Key 的泄漏

在上一讲中,我们分析了 ThreadLocal 的内部结构,知道了每一个 Thread 都有一个  ThreadLocal.ThreadLocalMap 这样的类型变量,该变量的名字叫作 threadLocals。线程在访问了 ThreadLocal  之后,都会在它的 ThreadLocalMap 里面的 Entry 中去维护该 ThreadLocal 变量与具体实例的映射。

我们可能会在业务代码中执行了 ThreadLocal instance = null 操作,想清理掉这个 ThreadLocal 实例,但是假设我们在  ThreadLocalMap 的 Entry 中强引用了 ThreadLocal 实例,那么,虽然在业务代码中把 ThreadLocal 实例置为了  null,但是在 Thread 类中依然有这个引用链的存在。

GC 在垃圾回收的时候会进行可达性分析,它会发现这个 ThreadLocal 对象依然是可达的,所以对于这个 ThreadLocal  对象不会进行垃圾回收,这样的话就造成了内存泄漏的情况。

jdk 开发者考虑到了这一点,所以 ThreadLocalMap 中的 Entry 继承了 WeakReference 弱引用,代码如下所示:

static class Entry extends WeakReference<ThreadLocal<?>> {          Object value;      Entry(ThreadLocal<?> k, Object v) {         super(k);         value = v;     } }

可以看到,这个 Entry 是 extends  WeakReference。弱引用的特点是,如果这个对象只被弱引用关联,而没有任何强引用关联,那么这个对象就可以被回收,所以弱引用不会阻止  GC。因此,这个弱引用的机制就避免了 ThreadLocal 的内存泄露问题。

这就是为什么 Entry 的 key 要使用弱引用的原因。

Value 的泄漏

可是,如果我们继续研究的话会发现,虽然 ThreadLocalMap 的每个 Entry 都是一个对 key 的弱引用,但是这个  Entry 包含了一个对 value 的强引用,还是刚才那段代码:

static class Entry extends WeakReference<ThreadLocal<?>> {          Object value;       Entry(ThreadLocal<?> k, Object v) {         super(k);         value = v;     } }

可以看到,value = v 这行代码就代表了强引用的发生。

正常情况下,当线程终止,key 所对应的 value  是可以被正常垃圾回收的,因为没有任何强引用存在了。但是有时线程的生命周期是很长的,如果线程迟迟不会终止,那么可能 ThreadLocal 以及它所对应的  value 早就不再有用了。在这种情况下,我们应该保证它们都能够被正常的回收。

为了更好地分析这个问题,我们用下面这张图来看一下具体的引用链路(实线代表强引用,虚线代表弱引用):

为什么每次用完ThreadLocal都要调用remove()

可以看到,左侧是引用栈,栈里面有一个 ThreadLocal 的引用和一个线程的引用,右侧是我们的堆,在堆中是对象的实例。

我们重点看一下下面这条链路:Thread Ref &rarr; Current Thread &rarr; ThreadLocalMap &rarr; Entry &rarr; Value &rarr;  可能泄漏的value实例。

这条链路是随着线程的存在而一直存在的,如果线程执行耗时任务而不停止,那么当垃圾回收进行可达性分析的时候,这个 Value  就是可达的,所以不会被回收。但是与此同时可能我们已经完成了业务逻辑处理,不再需要这个 Value 了,此时也就发生了内存泄漏问题。

JDK 同样也考虑到了这个问题,在执行 ThreadLocal 的 set、remove、rehash 等方法时,它都会扫描 key 为 null 的  Entry,如果发现某个 Entry 的 key 为 null,则代表它所对应的 value 也没有作用了,所以它就会把对应的 value 置为  null,这样,value 对象就可以被正常回收了。

但是假设 ThreadLocal 已经不被使用了,那么实际上 set、remove、rehash  方法也不会被调用,与此同时,如果这个线程又一直存活、不终止的话,那么刚才的那个调用链就一直存在,也就导致了 value 的内存泄漏。

如何避免内存泄露

分析完这个问题之后,该如何解决呢?解决方法就是我们本课时的标题:调用 ThreadLocal 的 remove  方法。调用这个方法就可以删除对应的 value 对象,可以避免内存泄漏。

我们来看一下 remove 方法的源码

public void remove() {     ThreadLocalMap m = getMap(Thread.currentThread());     if (m != null)         m.remove(this); }

可以看出,它是先获取到 ThreadLocalMap 这个引用的,并且调用了它的 remove 方法。这里的 remove 方法可以把 key 所对应的  value 给清理掉,这样一来,value 就可以被 GC 回收了。

所以,在使用完了 ThreadLocal 之后,我们应该手动去调用它的 remove 方法,目的是防止内存泄漏的发生。

到此,关于“为什么每次用完ThreadLocal都要调用remove()”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: 为什么每次用完ThreadLocal都要调用remove()

本文链接: https://www.lsjlt.com/news/83469.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 为什么每次用完ThreadLocal都要调用remove()
    这篇文章主要介绍“为什么每次用完ThreadLocal都要调用remove()”,在日常操作中,相信很多人在为什么每次用完ThreadLocal都要调用remove()问题上存在疑惑,小编查阅了各式资料,整...
    99+
    2024-04-02
  • 每次开机都要自检是什么原因如何解决
    每次开机都要进行自检的原因可能是由于硬件故障、操作系统问题或者设置错误导致的。要解决这个问题,可以尝试以下方法:1. 检查硬件:首先...
    99+
    2023-09-07
    解决
  • 电脑每次开机都要磁盘检查的原因是什么
    这篇文章主要介绍了电脑每次开机都要磁盘检查的原因是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇电脑每次开机都要磁盘检查的原因是什么文章都会有所收获,下面我们一起来看看吧。电脑每次开机都要磁盘检查的原因:一...
    99+
    2023-07-02
  • linux下解决 git clone每次都要输入用户名密码问题(推荐)
    目录一、背景:二、解决方法:1、ssh方式:2、免密拉取配置3、粗暴使用型三、总结:一、背景: git clone代码或者push代码时候需要输入账号密码 二、解决方法: 1、ssh...
    99+
    2022-11-13
    git clone每次都要输入用户名密码 git clone输入密码 git clone用户名
  • Win10每一次打开应用都要弹出是不是容许更改该怎么办?
    坚信许多消费者在打开软件是时候都是会弹出那样一个实际操作否容许更改的提醒,这让许多客户好烦,那麼应当如何把这一提醒关闭呢?大伙儿寻找系统软件和安全性作用开启,点击在其中的安全可靠和维护保养选择项,随后开启用户帐户控制设定对话框,以后将通告滚...
    99+
    2023-07-10
  • 为什么建站都用linux
    使用linux建站的优势有以下几点Linux能看到源代码,可以自行剪裁和定制所需的内核模块,有利于系统内核管理优化和驱动程序的开发。Linux是基于GPL的基础下的产物,所以Linux是开源免费的,任何人都可以自由使用Linux。Linux...
    99+
    2024-04-02
  • 怎么用Meta取消流量器缓存实现每次访问都刷新页面方便调试
    这篇文章主要介绍“怎么用Meta取消流量器缓存实现每次访问都刷新页面方便调试”,在日常操作中,相信很多人在怎么用Meta取消流量器缓存实现每次访问都刷新页面方便调试问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答...
    99+
    2023-06-08
  • 剖析Java中在Collection集合中使用contains和remove为什么要重写equals
    目录引言源码剖析实例测试String类和包装类的特殊情况自定义类型总结引言 在Collection集合中: contains方法是判断一个集合里面是否包含指定元素,如果有则返回tru...
    99+
    2024-04-02
  • 为什么要用ecshop
    用ecshop的原因:1、具备丰富的功能和灵活的配置选项;2、拥有强大的后台管理功能;3、具备良好的用户体验和友好的界面设计;4、有强大的社区支持和技术团队;5、提供了完善的售后服务和技术支持。本文的操作环境:Windows10系统、ECS...
    99+
    2023-07-13
  • 为什么不要PySnoope中使用print进行调试
    为什么不要PySnoope中使用print进行调试?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。PySnooper 是一个非常方便的调试器。如果您正在试图弄清楚为什么您的Pyt...
    99+
    2023-06-06
  • 为什么要使用MySQL
    这篇文章给大家分享的是有关为什么要使用MySQL的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是MySQL  MySQL原本是一个开放源码的关系数据库管理系统,原开发者为瑞典...
    99+
    2024-04-02
  • 为什么要使用Hive
    这篇文章将为大家详细讲解有关为什么要使用Hive,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是HiveHive:由Facebook开源用于解决海量结构化日志的数据统计。 Hive是基于Ha...
    99+
    2023-06-02
  • 为什么要少用Iframe
    今天给大家介绍一下为什么要少用Iframe。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。下图显示创建 100 个不同的元素中iframe到底有多耗费时间。 创...
    99+
    2023-06-08
  • 为什么要使用GraphQL
    这篇文章主要介绍“为什么要使用GraphQL”,在日常操作中,相信很多人在为什么要使用GraphQL问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”为什么要使用GraphQL”...
    99+
    2024-04-02
  • 为什么要使用String
    本篇文章为大家展示了为什么要使用String,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。最近在培训课期间指导初学者。任务之一就是要大家完成一个类,要求这个类对key为String类型的map执行d...
    99+
    2023-06-17
  • 为什么不要在using语句中调用WCF服务
    这篇文章主要介绍为什么不要在using语句中调用WCF服务,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!如果你调用WCF服务时,像下面的代码这样在using语句中进行调用,需要注意一个问题。using (C...
    99+
    2023-06-17
  • 为什么要使用JRebel
    这篇文章给大家分享的是有关为什么要使用JRebel的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。为什么要使用JRebel?  在开发过程中有一个很头疼的问题:每次修改后台代码之后,都需要重新将...
    99+
    2023-06-04
  • 为什么要使用video.js
    这篇文章主要介绍为什么要使用video.js,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!为什么要使用video.js? PC端浏览器并不支持video直接播放m3u8格式的视频 手机端各式各样的浏览器定制的vide...
    99+
    2023-06-09
  • 为什么要使用redis
    这篇文章给大家分享的是有关为什么要使用redis的内容。小编觉得挺实用的,因此分享给大家做个参考。一起跟随小编过来看看吧。为什么要使用redis?redis数据库是将数据存储在内存中的,并且读写内存的速度要...
    99+
    2024-04-02
  • 为什么要使用docker
    小编给大家分享一下为什么要使用docker,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!    一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。做为开发+运维之间的协作,...
    99+
    2023-06-04
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作